From 1d10f3486441f4d40f7de44ac7f3601634157094 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:46:58 -0400 Subject: [PATCH 001/126] Remove 'network' from Snap plugs (#11606) --- apps/desktop/electron-builder.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index be30e063c1a..0e621e8a1f9 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -249,7 +249,7 @@ "autoStart": true, "base": "core22", "confinement": "strict", - "plugs": ["default", "network", "network-bind", "password-manager-service"], + "plugs": ["default", "network-bind", "password-manager-service"], "stagePackages": ["default"] }, "protocols": [ From 133257f60e7dd256388338bff3aaca451199ade4 Mon Sep 17 00:00:00 2001 From: Opeyemi Date: Thu, 17 Oct 2024 19:40:42 +0100 Subject: [PATCH 002/126] clean up document start (#11607) --- .github/workflows/auto-branch-updater.yml | 1 - .github/workflows/automatic-issue-responses.yml | 1 - .github/workflows/automatic-pull-request-responses.yml | 1 - .github/workflows/brew-bump-desktop.yml | 1 - .github/workflows/build-browser.yml | 1 - .github/workflows/build-cli.yml | 3 +-- .github/workflows/build-desktop.yml | 1 - .github/workflows/build-web.yml | 1 - .github/workflows/chromatic.yml | 1 - .github/workflows/crowdin-pull.yml | 1 - .github/workflows/deploy-web.yml | 1 - .github/workflows/enforce-labels.yml | 1 - .github/workflows/lint.yml | 1 - .github/workflows/locales-lint.yml | 1 - .github/workflows/publish-cli.yml | 1 - .github/workflows/publish-desktop.yml | 3 +-- .github/workflows/publish-web.yml | 1 - .github/workflows/release-browser.yml | 1 - .github/workflows/release-cli.yml | 1 - .github/workflows/release-desktop-beta.yml | 1 - .github/workflows/release-desktop.yml | 1 - .github/workflows/release-web.yml | 1 - .github/workflows/retrieve-current-desktop-rollout.yml | 1 - .github/workflows/scan.yml | 1 - .github/workflows/staged-rollout-desktop.yml | 1 - .github/workflows/stale-bot.yml | 1 - .github/workflows/test.yml | 1 - .github/workflows/version-auto-bump.yml | 1 - .github/workflows/version-bump.yml | 1 - 29 files changed, 2 insertions(+), 31 deletions(-) diff --git a/.github/workflows/auto-branch-updater.yml b/.github/workflows/auto-branch-updater.yml index 7b69b4405ca..97f020fde7b 100644 --- a/.github/workflows/auto-branch-updater.yml +++ b/.github/workflows/auto-branch-updater.yml @@ -1,4 +1,3 @@ ---- name: Auto Update Branch on: diff --git a/.github/workflows/automatic-issue-responses.yml b/.github/workflows/automatic-issue-responses.yml index 289b8bd662b..e38f8103cb5 100644 --- a/.github/workflows/automatic-issue-responses.yml +++ b/.github/workflows/automatic-issue-responses.yml @@ -1,4 +1,3 @@ ---- name: Automatic issue responses on: issues: diff --git a/.github/workflows/automatic-pull-request-responses.yml b/.github/workflows/automatic-pull-request-responses.yml index 16e1a46c052..6bd069d21ac 100644 --- a/.github/workflows/automatic-pull-request-responses.yml +++ b/.github/workflows/automatic-pull-request-responses.yml @@ -1,4 +1,3 @@ ---- name: Automatic pull request responses on: pull_request: diff --git a/.github/workflows/brew-bump-desktop.yml b/.github/workflows/brew-bump-desktop.yml index 31239aa9b29..1b3c99128bf 100644 --- a/.github/workflows/brew-bump-desktop.yml +++ b/.github/workflows/brew-bump-desktop.yml @@ -1,4 +1,3 @@ ---- name: Bump Desktop Cask on: diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 0e59610bb26..20a36dc5b23 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -1,4 +1,3 @@ ---- name: Build Browser on: diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 26d30fb5b90..f88c4767407 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -1,4 +1,3 @@ ---- name: Build CLI on: @@ -289,7 +288,7 @@ jobs: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - + - name: Zip NPM Build Artifact run: Get-ChildItem -Path .\build | Compress-Archive -DestinationPath .\bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 7f01d1fa66c..2c89e0d156f 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -1,4 +1,3 @@ ---- name: Build Desktop on: diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 21b976f9120..ec09f25ac19 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -1,4 +1,3 @@ ---- name: Build Web on: diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 0352c0ca2ea..d6f63d48032 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -1,4 +1,3 @@ ---- name: Chromatic on: diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 974e30c28e6..dfcd3294b01 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -1,4 +1,3 @@ ---- name: Crowdin Sync on: diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index d0f791aa000..5cc4eb90861 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -1,4 +1,3 @@ ---- name: Deploy Web Vault run-name: Deploy Web Vault to ${{ inputs.environment }} from ${{ inputs.branch-or-tag }} diff --git a/.github/workflows/enforce-labels.yml b/.github/workflows/enforce-labels.yml index a98c4ae1bea..40ddfe7739f 100644 --- a/.github/workflows/enforce-labels.yml +++ b/.github/workflows/enforce-labels.yml @@ -1,4 +1,3 @@ ---- name: Enforce PR labels on: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b06365fa975..db7fef83fb8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,3 @@ ---- name: Lint on: diff --git a/.github/workflows/locales-lint.yml b/.github/workflows/locales-lint.yml index a701d63d10f..ef944526111 100644 --- a/.github/workflows/locales-lint.yml +++ b/.github/workflows/locales-lint.yml @@ -1,4 +1,3 @@ ---- name: Locales lint for Crowdin on: diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index a54d70b8d39..c9a4e841ea8 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -1,4 +1,3 @@ ---- name: Publish CLI run-name: Publish CLI ${{ inputs.publish_type }} diff --git a/.github/workflows/publish-desktop.yml b/.github/workflows/publish-desktop.yml index 8103dd7b7cc..c46a7a27601 100644 --- a/.github/workflows/publish-desktop.yml +++ b/.github/workflows/publish-desktop.yml @@ -1,4 +1,3 @@ ---- name: Publish Desktop run-name: Publish Desktop ${{ inputs.publish_type }} @@ -125,7 +124,7 @@ jobs: secrets: "aws-electron-access-id, aws-electron-access-key, aws-electron-bucket-name" - + - name: Create artifacts directory run: mkdir -p apps/desktop/artifacts diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index e26d536aada..7e0e8737344 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -1,4 +1,3 @@ ---- name: Publish Web run-name: Publish Web ${{ inputs.publish_type }} diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index 860acce2b7d..aed9ab293e8 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -1,4 +1,3 @@ ---- name: Release Browser run-name: Release Browser ${{ inputs.release_type }} diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 60e5bc025bb..8660744f944 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -1,4 +1,3 @@ ---- name: Release CLI run-name: Release CLI ${{ inputs.release_type }} diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index 19b3ac2d229..7518daf0b16 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -1,4 +1,3 @@ ---- name: Release Desktop Beta on: diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 7c5228478af..b0ddc4b804d 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -1,4 +1,3 @@ ---- name: Release Desktop run-name: Release Desktop ${{ inputs.release_type }} diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 7f18242909f..e3462a98fb6 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -1,4 +1,3 @@ ---- name: Release Web run-name: Release Web ${{ inputs.release_type }} diff --git a/.github/workflows/retrieve-current-desktop-rollout.yml b/.github/workflows/retrieve-current-desktop-rollout.yml index 45a2bf5ce42..2ab3072f566 100644 --- a/.github/workflows/retrieve-current-desktop-rollout.yml +++ b/.github/workflows/retrieve-current-desktop-rollout.yml @@ -1,4 +1,3 @@ ---- name: Retrieve Current Desktop Rollout on: diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 5afd133afd4..143d049bd63 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -1,4 +1,3 @@ ---- name: Scan on: diff --git a/.github/workflows/staged-rollout-desktop.yml b/.github/workflows/staged-rollout-desktop.yml index 1f751507640..91250a443f2 100644 --- a/.github/workflows/staged-rollout-desktop.yml +++ b/.github/workflows/staged-rollout-desktop.yml @@ -1,4 +1,3 @@ ---- name: Staged Rollout Desktop on: diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index 4d3085ce003..6caa7b99331 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -1,4 +1,3 @@ ---- name: 'Close stale issues and PRs' on: workflow_dispatch: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0f3cfd56c94..4ea08a24373 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,3 @@ ---- name: Testing on: diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index f10abee300d..cc6feeba026 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -1,4 +1,3 @@ ---- name: Auto Bump Desktop Version on: diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 457cdd08fe0..7f6dfef79cf 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -1,4 +1,3 @@ ---- name: Version Bump on: From c7bee5c55d33b6529468ebd48605fc47fcc5003a Mon Sep 17 00:00:00 2001 From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:07:18 -0400 Subject: [PATCH 003/126] PM-11205 updating empty state description to match figma (#11414) --- .../vault/org-vault/collection-access-restricted.component.ts | 2 +- apps/web/src/locales/en/messages.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts b/apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts index eb81fa196d5..1e4280626fe 100644 --- a/apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts +++ b/apps/web/src/app/vault/org-vault/collection-access-restricted.component.ts @@ -15,7 +15,7 @@ const icon = svgIcon` - {{ "collectionAccessRestricted" | i18n }} + {{ "youDoNotHavePermissions" | i18n }} - - - - -
-

{{ "noPasswordsInList" | i18n }}

-
+ + + + + +
+

{{ "noPasswordsInList" | i18n }}

+
diff --git a/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts b/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts new file mode 100644 index 00000000000..8772a245821 --- /dev/null +++ b/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts @@ -0,0 +1,97 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { BehaviorSubject } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { 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 { ColorPasswordModule, ItemModule, ToastService } from "@bitwarden/components"; +import { ColorPasswordComponent } from "@bitwarden/components/src/color-password/color-password.component"; + +import { PasswordHistoryViewComponent } from "./password-history-view.component"; + +describe("PasswordHistoryViewComponent", () => { + let component: PasswordHistoryViewComponent; + let fixture: ComponentFixture; + + const mockCipher = { + id: "122-333-444", + type: CipherType.Login, + organizationId: "222-444-555", + } as CipherView; + + const copyToClipboard = jest.fn(); + const showToast = jest.fn(); + const activeAccount$ = new BehaviorSubject<{ id: string }>({ id: "666-444-444" }); + const mockCipherService = { + get: jest.fn().mockResolvedValue({ decrypt: jest.fn().mockResolvedValue(mockCipher) }), + getKeyForCipherKeyDecryption: jest.fn().mockResolvedValue({}), + }; + + beforeEach(async () => { + mockCipherService.get.mockClear(); + mockCipherService.getKeyForCipherKeyDecryption.mockClear(); + copyToClipboard.mockClear(); + showToast.mockClear(); + + await TestBed.configureTestingModule({ + imports: [ItemModule, ColorPasswordModule, JslibModule], + providers: [ + { provide: WINDOW, useValue: window }, + { provide: CipherService, useValue: mockCipherService }, + { provide: PlatformUtilsService, useValue: { copyToClipboard } }, + { provide: AccountService, useValue: { activeAccount$ } }, + { provide: ToastService, useValue: { showToast } }, + { provide: I18nService, useValue: { t: (key: string) => key } }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(PasswordHistoryViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("renders no history text when history does not exist", () => { + expect(fixture.debugElement.nativeElement.textContent).toBe("noPasswordsInList"); + }); + + describe("history", () => { + const password1 = { password: "bad-password-1", lastUsedDate: new Date("09/13/2004") }; + const password2 = { password: "bad-password-2", lastUsedDate: new Date("02/01/2004") }; + + beforeEach(async () => { + mockCipher.passwordHistory = [password1, password2]; + + mockCipherService.get.mockResolvedValue({ decrypt: jest.fn().mockResolvedValue(mockCipher) }); + await component.ngOnInit(); + fixture.detectChanges(); + }); + + it("renders all passwords", () => { + const passwords = fixture.debugElement.queryAll(By.directive(ColorPasswordComponent)); + + expect(passwords.map((password) => password.componentInstance.password)).toEqual([ + "bad-password-1", + "bad-password-2", + ]); + }); + + it("copies a password", () => { + const copyButton = fixture.debugElement.query(By.css("button")); + + copyButton.nativeElement.click(); + + expect(copyToClipboard).toHaveBeenCalledWith("bad-password-1", { window: window }); + expect(showToast).toHaveBeenCalledWith({ + message: "passwordCopied", + title: "", + variant: "info", + }); + }); + }); +}); diff --git a/libs/vault/src/components/password-history-view/password-history-view.component.ts b/libs/vault/src/components/password-history-view/password-history-view.component.ts new file mode 100644 index 00000000000..5e858af7275 --- /dev/null +++ b/libs/vault/src/components/password-history-view/password-history-view.component.ts @@ -0,0 +1,77 @@ +import { CommonModule } from "@angular/common"; +import { OnInit, Inject, Component, Input } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { CipherId, UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { PasswordHistoryView } from "@bitwarden/common/vault/models/view/password-history.view"; +import { + ToastService, + ItemModule, + ColorPasswordModule, + IconButtonModule, +} from "@bitwarden/components"; + +@Component({ + selector: "vault-password-history-view", + templateUrl: "./password-history-view.component.html", + standalone: true, + imports: [CommonModule, ItemModule, ColorPasswordModule, IconButtonModule, JslibModule], +}) +export class PasswordHistoryViewComponent implements OnInit { + /** + * The ID of the cipher to display the password history for. + */ + @Input({ required: true }) cipherId: CipherId; + + /** The password history for the cipher. */ + history: PasswordHistoryView[] = []; + + constructor( + @Inject(WINDOW) private win: Window, + protected cipherService: CipherService, + protected platformUtilsService: PlatformUtilsService, + protected i18nService: I18nService, + protected accountService: AccountService, + protected toastService: ToastService, + ) {} + + async ngOnInit() { + await this.init(); + } + + /** Copies a password to the clipboard. */ + copy(password: string) { + const copyOptions = this.win != null ? { window: this.win } : undefined; + this.platformUtilsService.copyToClipboard(password, copyOptions); + this.toastService.showToast({ + variant: "info", + title: "", + message: this.i18nService.t("passwordCopied"), + }); + } + + /** Retrieve the password history for the given cipher */ + protected async init() { + const cipher = await this.cipherService.get(this.cipherId); + const activeAccount = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a: { id: string | undefined }) => a)), + ); + + if (!activeAccount?.id) { + throw new Error("Active account is not available."); + } + + const activeUserId = activeAccount.id as UserId; + const decCipher = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), + ); + + this.history = decCipher.passwordHistory == null ? [] : decCipher.passwordHistory; + } +} diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts index d5841c7db06..f6a95281f81 100644 --- a/libs/vault/src/index.ts +++ b/libs/vault/src/index.ts @@ -12,5 +12,6 @@ export { } from "./components/assign-collections.component"; export { DownloadAttachmentComponent } from "./components/download-attachment/download-attachment.component"; +export { PasswordHistoryViewComponent } from "./components/password-history-view/password-history-view.component"; export * as VaultIcons from "./icons"; From 80a4fba7871d2133a5f799c52686adcfe84e10d2 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:57:34 -0500 Subject: [PATCH 017/126] Allow for the web generation dialog to disable the margin of the tools generation components (#11565) --- .../web-generator-dialog/web-generator-dialog.component.html | 1 + .../components/src/passphrase-settings.component.html | 2 +- .../generator/components/src/passphrase-settings.component.ts | 4 ++++ .../components/src/password-generator.component.html | 2 ++ .../generator/components/src/password-generator.component.ts | 4 ++++ .../generator/components/src/password-settings.component.html | 2 +- .../generator/components/src/password-settings.component.ts | 4 ++++ .../components/src/username-generator.component.html | 4 ++-- .../generator/components/src/username-generator.component.ts | 4 ++++ .../cipher-generator/cipher-form-generator.component.html | 2 ++ .../cipher-generator/cipher-form-generator.component.ts | 4 ++++ 11 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.html b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.html index afe62cdc8a2..e224d1d19cc 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.html +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.html @@ -6,6 +6,7 @@ diff --git a/libs/tools/generator/components/src/passphrase-settings.component.html b/libs/tools/generator/components/src/passphrase-settings.component.html index c40df97c69c..2a3f4b5a287 100644 --- a/libs/tools/generator/components/src/passphrase-settings.component.html +++ b/libs/tools/generator/components/src/passphrase-settings.component.html @@ -1,4 +1,4 @@ - +
{{ "options" | i18n }}
diff --git a/libs/tools/generator/components/src/passphrase-settings.component.ts b/libs/tools/generator/components/src/passphrase-settings.component.ts index 25e028210cc..82524eba4d8 100644 --- a/libs/tools/generator/components/src/passphrase-settings.component.ts +++ b/libs/tools/generator/components/src/passphrase-settings.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, skip, takeUntil, Subject } from "rxjs"; @@ -47,6 +48,9 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy { @Input() showHeader: boolean = true; + /** Removes bottom margin from `bit-section` */ + @Input({ transform: coerceBooleanProperty }) disableMargin = false; + /** Emits settings updates and completes if the settings become unavailable. * @remarks this does not emit the initial settings. If you would like * to receive live settings updates including the initial update, diff --git a/libs/tools/generator/components/src/password-generator.component.html b/libs/tools/generator/components/src/password-generator.component.html index 9a33aa143ec..b4cf8c6cdb6 100644 --- a/libs/tools/generator/components/src/password-generator.component.html +++ b/libs/tools/generator/components/src/password-generator.component.html @@ -32,6 +32,7 @@ class="tw-mt-6" *ngIf="(algorithm$ | async)?.id === 'password'" [userId]="this.userId$ | async" + [disableMargin]="disableMargin" (onUpdated)="generate$.next()" /> diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts index bf33c7cfca9..e3f9073cb1e 100644 --- a/libs/tools/generator/components/src/password-generator.component.ts +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core"; import { BehaviorSubject, @@ -45,6 +46,9 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy { @Input() userId: UserId | null; + /** Removes bottom margin, passed to downstream components */ + @Input({ transform: coerceBooleanProperty }) disableMargin = false; + /** tracks the currently selected credential type */ protected credentialType$ = new BehaviorSubject(null); diff --git a/libs/tools/generator/components/src/password-settings.component.html b/libs/tools/generator/components/src/password-settings.component.html index fcafc789049..9c4fb595392 100644 --- a/libs/tools/generator/components/src/password-settings.component.html +++ b/libs/tools/generator/components/src/password-settings.component.html @@ -1,4 +1,4 @@ - +

{{ "options" | i18n }}

diff --git a/libs/tools/generator/components/src/password-settings.component.ts b/libs/tools/generator/components/src/password-settings.component.ts index 9466c81a0f4..2a8bff31c4a 100644 --- a/libs/tools/generator/components/src/password-settings.component.ts +++ b/libs/tools/generator/components/src/password-settings.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, takeUntil, Subject, map, filter, tap, debounceTime, skip } from "rxjs"; @@ -55,6 +56,9 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy { @Input() waitMs: number = 100; + /** Removes bottom margin from `bit-section` */ + @Input({ transform: coerceBooleanProperty }) disableMargin = false; + /** Emits settings updates and completes if the settings become unavailable. * @remarks this does not emit the initial settings. If you would like * to receive live settings updates including the initial update, diff --git a/libs/tools/generator/components/src/username-generator.component.html b/libs/tools/generator/components/src/username-generator.component.html index 6425cb7a38f..e9d7d1c1f8c 100644 --- a/libs/tools/generator/components/src/username-generator.component.html +++ b/libs/tools/generator/components/src/username-generator.component.html @@ -17,11 +17,11 @@ - +
{{ "options" | i18n }}
-
+
diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts index 767c73c398a..fd1a21cc3e9 100644 --- a/libs/tools/generator/components/src/username-generator.component.ts +++ b/libs/tools/generator/components/src/username-generator.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { @@ -57,6 +58,9 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { @Output() readonly onGenerated = new EventEmitter(); + /** Removes bottom margin from internal elements */ + @Input({ transform: coerceBooleanProperty }) disableMargin = false; + /** Tracks the selected generation algorithm */ protected credential = this.formBuilder.group({ type: [null as CredentialAlgorithm], diff --git a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.html b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.html index 181ca50da8a..445908679c3 100644 --- a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.html +++ b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.html @@ -1,8 +1,10 @@ diff --git a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts index db6e9ae106b..79fac29d4d9 100644 --- a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { CommonModule } from "@angular/common"; import { Component, EventEmitter, Input, Output } from "@angular/core"; @@ -21,6 +22,9 @@ export class CipherFormGeneratorComponent { @Input({ required: true }) type: "password" | "username"; + /** Removes bottom margin of internal sections */ + @Input({ transform: coerceBooleanProperty }) disableMargin = false; + /** * Emits an event when a new value is generated. */ From 82547573752a0e5ecbdcd8094c45207a0a46bc48 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:28:42 -0700 Subject: [PATCH 018/126] [PM-13453] - Health report raw data with member count component (#11573) * add raw data + members table * remove commented code --- .../access-intelligence.component.html | 3 + .../access-intelligence.component.ts | 2 + .../password-health-members.component.html | 61 +++++ .../password-health-members.component.ts | 233 ++++++++++++++++++ .../password-health.mock.ts | 66 +++++ 5 files changed, 365 insertions(+) create mode 100644 apps/web/src/app/tools/access-intelligence/password-health-members.component.html create mode 100644 apps/web/src/app/tools/access-intelligence/password-health-members.component.ts create mode 100644 apps/web/src/app/tools/access-intelligence/password-health.mock.ts diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html index df3eee389f6..78ddfb23929 100644 --- a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html +++ b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html @@ -3,6 +3,9 @@ + + + - {{ "noMatchingLoginsForSite" | i18n }} - {{ "searchSavePasskeyNewLogin" | i18n }} + {{ + (hasSearched ? "noItemsMatchSearch" : "noMatchingLoginsForSite") | i18n + }} + {{ + (hasSearched ? "searchSavePasskeyNewLogin" : "clearFiltersOrTryAnother") | i18n + }} + @@ -100,17 +105,22 @@

{{ "chooseCipherForPasskeySave" | i18n }}

- {{ "noItemsMatchSearch" | i18n }} - {{ "clearFiltersOrTryAnother" | i18n }} + {{ + (hasSearched ? "noItemsMatchSearch" : "noMatchingLoginsForSite") | i18n + }} + {{ + (hasSearched ? "searchSavePasskeyNewLogin" : "clearFiltersOrTryAnother") | i18n + }} + diff --git a/apps/browser/src/autofill/popup/fido2/fido2.component.ts b/apps/browser/src/autofill/popup/fido2/fido2.component.ts index cf0fd90a8fd..82be95ea0da 100644 --- a/apps/browser/src/autofill/popup/fido2/fido2.component.ts +++ b/apps/browser/src/autofill/popup/fido2/fido2.component.ts @@ -91,7 +91,6 @@ interface ViewData { export class Fido2Component implements OnInit, OnDestroy { private destroy$ = new Subject(); private message$ = new BehaviorSubject(null); - private hasSearched = false; protected BrowserFido2MessageTypes = BrowserFido2MessageTypes; protected cipher: CipherView; protected ciphers?: CipherView[] = []; @@ -104,6 +103,7 @@ export class Fido2Component implements OnInit, OnDestroy { protected noResultsIcon = Icons.NoResults; protected passkeyAction: PasskeyActionValue = PasskeyActions.Register; protected PasskeyActions = PasskeyActions; + protected hasSearched = false; protected searchText: string; protected searchTypeSearch = false; protected senderTabId?: string; @@ -370,19 +370,30 @@ export class Fido2Component implements OnInit, OnDestroy { return this.equivalentDomains; } + async clearSearch() { + this.searchText = ""; + await this.setDisplayedCiphersToAllDomainMatch(); + } + + protected async setDisplayedCiphersToAllDomainMatch() { + const equivalentDomains = await this.getEquivalentDomains(); + this.displayedCiphers = this.ciphers.filter((cipher) => + cipher.login.matchesUri(this.url, equivalentDomains), + ); + } + protected async search() { - this.hasSearched = await this.searchService.isSearchable(this.searchText); - if (this.hasSearched) { + this.hasSearched = true; + const isSearchable = await this.searchService.isSearchable(this.searchText); + + if (isSearchable) { this.displayedCiphers = await this.searchService.searchCiphers( this.searchText, null, this.ciphers, ); } else { - const equivalentDomains = await this.getEquivalentDomains(); - this.displayedCiphers = this.ciphers.filter((cipher) => - cipher.login.matchesUri(this.url, equivalentDomains), - ); + await this.setDisplayedCiphersToAllDomainMatch(); } } From c9de05de95220db60f967432d4fbc798b4d6a0e6 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Mon, 21 Oct 2024 09:50:59 -0400 Subject: [PATCH 023/126] [PM-13675] Adjust browser autofill override instructions conditions and placement in the settings view (#11559) * adjust browser autofill override instructions conditions and placement in the settings view * adjust placement of override instructions in the refresh component for Firefox --- .../popup/settings/autofill-v1.component.html | 17 ++++++++-- .../popup/settings/autofill.component.html | 31 ++++++++++--------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/browser/src/autofill/popup/settings/autofill-v1.component.html b/apps/browser/src/autofill/popup/settings/autofill-v1.component.html index ec8aeac37e9..530519e88f1 100644 --- a/apps/browser/src/autofill/popup/settings/autofill-v1.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill-v1.component.html @@ -41,8 +41,19 @@

-
@@ -86,7 +97,7 @@

/> -

- + {{ "autofillSuggestionsSectionTitle" | i18n }} > {{ "showInlineMenuOnFormFieldsDescAlt" | i18n }} + + {{ "turnOffBrowserBuiltInPasswordManagerSettings" | i18n }} + + {{ "turnOffBrowserBuiltInPasswordManagerSettingsLink" | i18n }} + + {{ "autofillSuggestionsSectionTitle" | i18n }} {{ "showInlineMenuOnIconSelectionLabel" | i18n }} - - {{ "turnOffBrowserBuiltInPasswordManagerSettings" | i18n }} - - {{ "turnOffBrowserBuiltInPasswordManagerSettingsLink" | i18n }} - - Date: Mon, 21 Oct 2024 16:10:57 +0200 Subject: [PATCH 024/126] [PM-13189] Send copy updates (#11619) * Add more descriptive header text * Update hint under optional password field --------- Co-authored-by: Daniel James Smith --- apps/browser/src/_locales/en/messages.json | 10 ++++++++-- .../add-edit/send-add-edit.component.ts | 19 +++++++++++++------ .../options/send-options.component.html | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 290663f4347..026d3b535ca 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2464,8 +2464,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -4494,9 +4494,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, diff --git a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts index 20b472f97f3..407a4d414a5 100644 --- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts +++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts @@ -174,18 +174,25 @@ export class SendAddEditComponent { ) .subscribe((config) => { this.config = config; - this.headerText = this.getHeaderText(config.mode); + this.headerText = this.getHeaderText(config.mode, config.sendType); }); } /** - * Gets the header text based on the mode. + * Gets the header text based on the mode and type. * @param mode The mode of the send form. + * @param type The type of the send * @returns The header text. */ - private getHeaderText(mode: SendFormMode) { - return this.i18nService.t( - mode === "edit" || mode === "partial-edit" ? "editSend" : "createSend", - ); + private getHeaderText(mode: SendFormMode, type: SendType) { + const headerKey = + mode === "edit" || mode === "partial-edit" ? "editItemHeader" : "newItemHeader"; + + switch (type) { + case SendType.Text: + return this.i18nService.t(headerKey, this.i18nService.t("textSend")); + case SendType.File: + return this.i18nService.t(headerKey, this.i18nService.t("fileSend")); + } } } diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html index 4da3466f708..265016ad1b1 100644 --- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html @@ -41,7 +41,7 @@

{{ "additionalOptions" | i18n }}

[appCopyClick]="sendOptionsForm.get('password').value" showToast > - {{ "sendPasswordDescV2" | i18n }} + {{ "sendPasswordDescV3" | i18n }} Date: Mon, 21 Oct 2024 17:56:50 +0200 Subject: [PATCH 025/126] =?UTF-8?q?[PM-12527]=20[Defect]=20Remove=20unnece?= =?UTF-8?q?ssary=20"General=20Information"=20Header=20in=20Organizat?= =?UTF-8?q?=E2=80=A6=20(#11198)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/billing/organizations/organization-plans.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index e6e2610d67c..16c5259e8ac 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -46,7 +46,7 @@ *ngIf="!loading && !selfHosted && this.passwordManagerPlans && this.secretsManagerPlans" class="tw-pt-6" > - + Date: Mon, 21 Oct 2024 12:07:28 -0400 Subject: [PATCH 026/126] [PM-13402] Adding the service to get member cipher details (#11544) * Adding the service to get member cipher details * Moving member cipher details to bitwarden license * Adding documentation to the api call --- .../reports/access-intelligence/index.ts | 0 .../member-cipher-details.response.ts | 16 +++ .../member-cipher-details-api.service.spec.ts | 105 ++++++++++++++++++ .../member-cipher-details-api.service.ts | 27 +++++ 4 files changed, 148 insertions(+) create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/response/member-cipher-details.response.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.spec.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.ts diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/response/member-cipher-details.response.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/response/member-cipher-details.response.ts new file mode 100644 index 00000000000..fcf5ada4b2c --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/response/member-cipher-details.response.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class MemberCipherDetailsResponse extends BaseResponse { + userName: string; + email: string; + useKeyConnector: boolean; + cipherIds: string[] = []; + + constructor(response: any) { + super(response); + this.userName = this.getResponseProperty("UserName"); + this.email = this.getResponseProperty("Email"); + this.useKeyConnector = this.getResponseProperty("UseKeyConnector"); + this.cipherIds = this.getResponseProperty("CipherIds"); + } +} diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.spec.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.spec.ts new file mode 100644 index 00000000000..b71abe075e6 --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.spec.ts @@ -0,0 +1,105 @@ +import { mock } from "jest-mock-extended"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; + +import { MemberCipherDetailsApiService } from "./member-cipher-details-api.service"; + +const mockMemberCipherDetails: any = { + data: [ + { + userName: "David Brent", + email: "david.brent@wernhamhogg.uk", + usesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab1", + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + ], + }, + { + userName: "Tim Canterbury", + email: "tim.canterbury@wernhamhogg.uk", + usesKeyConnector: false, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + ], + }, + { + userName: "Gareth Keenan", + email: "gareth.keenan@wernhamhogg.uk", + usesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + "cbea34a8-bde4-46ad-9d19-b05001227nm7", + ], + }, + { + userName: "Dawn Tinsley", + email: "dawn.tinsley@wernhamhogg.uk", + usesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + ], + }, + { + userName: "Keith Bishop", + email: "keith.bishop@wernhamhogg.uk", + usesKeyConnector: false, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab1", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + ], + }, + { + userName: "Chris Finch", + email: "chris.finch@wernhamhogg.uk", + usesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + ], + }, + ], +}; + +describe("Member Cipher Details API Service", () => { + let memberCipherDetailsApiService: MemberCipherDetailsApiService; + + const apiService = mock(); + + beforeEach(() => { + memberCipherDetailsApiService = new MemberCipherDetailsApiService(apiService); + jest.resetAllMocks(); + }); + + it("instantiates", () => { + expect(memberCipherDetailsApiService).not.toBeFalsy(); + }); + + it("getMemberCipherDetails retrieves data", async () => { + apiService.send.mockResolvedValue(mockMemberCipherDetails); + + const orgId = "1234"; + const result = await memberCipherDetailsApiService.getMemberCipherDetails(orgId); + expect(result).not.toBeNull(); + expect(result).toHaveLength(6); + expect(apiService.send).toHaveBeenCalledWith( + "GET", + "/reports/member-cipher-details/" + orgId, + null, + true, + true, + ); + }); +}); diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.ts new file mode 100644 index 00000000000..9351ac87776 --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-api.service.ts @@ -0,0 +1,27 @@ +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; + +import { MemberCipherDetailsResponse } from "../response/member-cipher-details.response"; + +export class MemberCipherDetailsApiService { + constructor(private apiService: ApiService) {} + + /** + * Returns a list of organization members with their assigned + * cipherIds + * @param orgId OrganizationId to get member cipher details for + * @returns List of organization members and assigned cipherIds + */ + async getMemberCipherDetails(orgId: string): Promise { + const response = await this.apiService.send( + "GET", + "/reports/member-cipher-details/" + orgId, + null, + true, + true, + ); + + const listResponse = new ListResponse(response, MemberCipherDetailsResponse); + return listResponse.data.map((r) => new MemberCipherDetailsResponse(r)); + } +} From 77c50860a9d6a2ece901d97488985208f0d6a2e8 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:30:25 -0400 Subject: [PATCH 027/126] [PM-12290] Show self-host options for CB MSP managed orgs (#11465) * (No Logic) organization-subscription-cloud.component.ts cleanup * Show only selfhost options for org owners and provider admins for managed orgs * Fix messages.json issue --- .../icons/manage-billing.icon.ts | 25 ---- .../icons/subscription-hidden.icon.ts | 24 ++++ ...nization-subscription-cloud.component.html | 111 +++++++++-------- ...ganization-subscription-cloud.component.ts | 112 +++++++----------- apps/web/src/locales/en/messages.json | 10 ++ .../organization-billing-metadata.response.ts | 2 + 6 files changed, 131 insertions(+), 153 deletions(-) delete mode 100644 apps/web/src/app/billing/organizations/icons/manage-billing.icon.ts create mode 100644 apps/web/src/app/billing/organizations/icons/subscription-hidden.icon.ts diff --git a/apps/web/src/app/billing/organizations/icons/manage-billing.icon.ts b/apps/web/src/app/billing/organizations/icons/manage-billing.icon.ts deleted file mode 100644 index 6f583bf2e81..00000000000 --- a/apps/web/src/app/billing/organizations/icons/manage-billing.icon.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { svgIcon } from "@bitwarden/components"; - -export const ManageBilling = svgIcon` - - - - - - - - - - - - - - - - - - - - - - `; diff --git a/apps/web/src/app/billing/organizations/icons/subscription-hidden.icon.ts b/apps/web/src/app/billing/organizations/icons/subscription-hidden.icon.ts new file mode 100644 index 00000000000..82490e82a1d --- /dev/null +++ b/apps/web/src/app/billing/organizations/icons/subscription-hidden.icon.ts @@ -0,0 +1,24 @@ +import { Icon, svgIcon } from "@bitwarden/components"; + +export const SubscriptionHiddenIcon: Icon = svgIcon` + + + + + + + + + + + + + + + + + + + + +`; diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index 643eeb93bad..0a83b2a56ce 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -1,17 +1,12 @@ - - - - {{ "loading" | i18n }} - - - + + + {{ "loading" | i18n }} + - + + {{ "secretsManager" | i18n }} > - -

- {{ "selfHostingTitle" | i18n }} -

-

- {{ "selfHostingEnterpriseOrganizationSectionCopy" | i18n }} - - -

-
- - -
+

{{ "additionalOptions" | i18n }}

@@ -302,13 +262,50 @@

{{ "additionalOptions" | i18n }}

- -
- - {{ - "manageBillingFromProviderPortalMessage" | i18n - }} -
-
+ + + +

{{ "manageSubscription" | i18n }}

+

+ {{ "manageSubscriptionFromThe" | i18n }} + {{ + "providerPortal" | i18n + }}. +

+ +

+ {{ "billingManagedByProvider" | i18n: userOrg.providerName }}. + {{ "billingContactProviderForAssistance" | i18n }}. +

+
+ +
+ +
+ +

{{ "billingManagedByProvider" | i18n: userOrg.providerName }}

+

{{ "billingContactProviderForAssistance" | i18n }}

+
+
+
+
+ + + +

+ {{ "selfHostingTitleProper" | i18n }} +

+

+ {{ "toHostBitwardenOnYourOwnServer" | i18n }} +

+
+ + +
+
+
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 7a66faa0a43..e604140e569 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 @@ -5,9 +5,9 @@ import { concatMap, firstValueFrom, lastValueFrom, Observable, Subject, takeUnti import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; -import { OrganizationApiKeyType, ProviderStatusType } from "@bitwarden/common/admin-console/enums"; +import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response"; @@ -15,7 +15,6 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { @@ -34,7 +33,7 @@ import { import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component"; import { ChangePlanDialogResultType, openChangePlanDialog } from "./change-plan-dialog.component"; import { DownloadLicenceDialogComponent } from "./download-license.component"; -import { ManageBilling } from "./icons/manage-billing.icon"; +import { SubscriptionHiddenIcon } from "./icons/subscription-hidden.icon"; import { SecretsManagerSubscriptionOptions } from "./sm-adjust-subscription.component"; @Component({ @@ -50,19 +49,17 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy hasBillingSyncToken: boolean; showAdjustSecretsManager = false; showSecretsManagerSubscribe = false; - firstLoaded = false; - loading: boolean; + loading = true; locale: string; showUpdatedSubscriptionStatusSection$: Observable; - manageBillingFromProviderPortal = ManageBilling; - isManagedByConsolidatedBillingMSP = false; enableTimeThreshold: boolean; preSelectedProductTier: ProductTierType = ProductTierType.Free; + showSubscription = true; + showSelfHost = false; + protected readonly subscriptionHiddenIcon = SubscriptionHiddenIcon; protected readonly teamsStarter = ProductTierType.TeamsStarter; - private destroy$ = new Subject(); - protected enableConsolidatedBilling$ = this.configService.getFeatureFlag$( FeatureFlag.EnableConsolidatedBilling, ); @@ -71,7 +68,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy FeatureFlag.EnableTimeThreshold, ); - protected EnableUpgradePasswordManagerSub$ = this.configService.getFeatureFlag$( + protected enableUpgradePasswordManagerSub$ = this.configService.getFeatureFlag$( FeatureFlag.EnableUpgradePasswordManagerSub, ); @@ -79,9 +76,10 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy FeatureFlag.AC2476_DeprecateStripeSourcesAPI, ); + private destroy$ = new Subject(); + constructor( private apiService: ApiService, - private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private logService: LogService, private organizationService: OrganizationService, @@ -89,15 +87,13 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy private route: ActivatedRoute, private dialogService: DialogService, private configService: ConfigService, - private providerService: ProviderService, private toastService: ToastService, + private billingApiService: BillingApiServiceAbstraction, ) {} async ngOnInit() { if (this.route.snapshot.queryParamMap.get("upgrade")) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.changePlan(); + await this.changePlan(); const productTierTypeStr = this.route.snapshot.queryParamMap.get("productTierType"); if (productTierTypeStr != null) { const productTier = Number(productTierTypeStr); @@ -112,7 +108,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy concatMap(async (params) => { this.organizationId = params.organizationId; await this.load(); - this.firstLoaded = true; }), takeUntil(this.destroy$), ) @@ -130,21 +125,34 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy } async load() { - if (this.loading) { - return; - } this.loading = true; this.locale = await firstValueFrom(this.i18nService.locale$); this.userOrg = await this.organizationService.get(this.organizationId); - if (this.userOrg.canViewSubscription) { - const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$); - const provider = await this.providerService.get(this.userOrg.providerId); - this.isManagedByConsolidatedBillingMSP = - enableConsolidatedBilling && - this.userOrg.hasProvider && - provider?.providerStatus == ProviderStatusType.Billable; + /* + +--------------------+--------------+----------------------+--------------+ + | User Type | Has Provider | Consolidated Billing | Subscription | + +--------------------+--------------+----------------------+--------------+ + | Organization Owner | False | N/A | Shown | + | Organization Owner | True | N/A | Hidden | + | Provider User | True | False | Shown | + | Provider User | True | True | Hidden | + +--------------------+--------------+----------------------+--------------+ + */ + + const consolidatedBillingEnabled = await firstValueFrom(this.enableConsolidatedBilling$); + this.showSubscription = + (!this.userOrg.hasProvider && this.userOrg.isOwner) || + (this.userOrg.hasProvider && this.userOrg.isProviderUser && !consolidatedBillingEnabled); + + const metadata = await this.billingApiService.getOrganizationBillingMetadata( + this.organizationId, + ); + + this.showSelfHost = metadata.isEligibleForSelfHost; + + if (this.showSubscription) { this.sub = await this.organizationApiService.getSubscription(this.organizationId); this.lineItems = this.sub?.subscription?.items; @@ -277,26 +285,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy return this.sub.subscription?.items.some((i) => i.sponsoredSubscriptionItem); } - get canDownloadLicense() { - return ( - (this.sub.planType !== PlanType.Free && this.subscription == null) || - (this.subscription != null && !this.subscription.cancelled) - ); - } - - get canManageBillingSync() { - return ( - this.sub.planType === PlanType.EnterpriseAnnually || - this.sub.planType === PlanType.EnterpriseMonthly || - this.sub.planType === PlanType.EnterpriseAnnually2023 || - this.sub.planType === PlanType.EnterpriseMonthly2023 || - this.sub.planType === PlanType.EnterpriseAnnually2020 || - this.sub.planType === PlanType.EnterpriseMonthly2020 || - this.sub.planType === PlanType.EnterpriseAnnually2019 || - this.sub.planType === PlanType.EnterpriseMonthly2019 - ); - } - get subscriptionDesc() { if (this.sub.planType === PlanType.Free) { return this.i18nService.t("subscriptionFreePlan", this.sub.seats.toString()); @@ -353,13 +341,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy ); } - shownSelfHost(): boolean { - return ( - this.sub?.plan.productTier !== ProductTierType.Teams && - this.sub?.plan.productTier !== ProductTierType.Free - ); - } - cancelSubscription = async () => { const reference = openOffboardingSurvey(this.dialogService, { data: { @@ -399,9 +380,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy title: null, message: this.i18nService.t("reinstated"), }); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); + await this.load(); } catch (e) { this.logService.error(e); } @@ -409,7 +388,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy async changePlan() { const EnableUpgradePasswordManagerSub = await firstValueFrom( - this.EnableUpgradePasswordManagerSub$, + this.enableUpgradePasswordManagerSub$, ); if (EnableUpgradePasswordManagerSub) { const reference = openChangePlanDialog(this.dialogService, { @@ -458,24 +437,15 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy }); await firstValueFrom(dialogRef.closed); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } - - closeDownloadLicense() { - this.showDownloadLicense = false; + await this.load(); } - subscriptionAdjusted() { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); + async subscriptionAdjusted() { + await this.load(); } calculateTotalAppliedDiscount(total: number) { - const discountedTotal = total / (1 - this.customerDiscount?.percentOff / 100); - return discountedTotal; + return total / (1 - this.customerDiscount?.percentOff / 100); } adjustStorage = (add: boolean) => { diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index d9be5d769bd..c50775efa6e 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9457,5 +9457,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/libs/common/src/billing/models/response/organization-billing-metadata.response.ts b/libs/common/src/billing/models/response/organization-billing-metadata.response.ts index 33d7907fa88..4831d290698 100644 --- a/libs/common/src/billing/models/response/organization-billing-metadata.response.ts +++ b/libs/common/src/billing/models/response/organization-billing-metadata.response.ts @@ -1,10 +1,12 @@ import { BaseResponse } from "../../../models/response/base.response"; export class OrganizationBillingMetadataResponse extends BaseResponse { + isEligibleForSelfHost: boolean; isOnSecretsManagerStandalone: boolean; constructor(response: any) { super(response); + this.isEligibleForSelfHost = this.getResponseProperty("IsEligibleForSelfHost"); this.isOnSecretsManagerStandalone = this.getResponseProperty("IsOnSecretsManagerStandalone"); } } From 4b9fbfc8326d7f1c714a7f4a0738484df06f2bad Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:45:07 -0700 Subject: [PATCH 028/126] [PM-13769] - fix routing for send created page (#11629) * fix routing for send created page * fix test --------- Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- .../popup/send-v2/add-edit/send-add-edit.component.ts | 4 ++-- .../send-v2/send-created/send-created.component.html | 4 ++-- .../send-created/send-created.component.spec.ts | 10 +++++++--- .../send-v2/send-created/send-created.component.ts | 8 +++++++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts index 407a4d414a5..585f6067e3d 100644 --- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts +++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts @@ -114,8 +114,8 @@ export class SendAddEditComponent { /** * Handles the event when the send is updated. */ - onSendUpdated(send: SendView) { - this.location.back(); + async onSendUpdated(_: SendView) { + await this.router.navigate(["/tabs/send"]); } deleteSend = async () => { diff --git a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html index 7c65cbeb17d..cdb514b8047 100644 --- a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html @@ -4,7 +4,7 @@ slot="header" [pageTitle]="'createdSend' | i18n" showBackButton - [backAction]="close.bind(this)" + [backAction]="goToEditSend.bind(this)" > @@ -27,7 +27,7 @@

{{ "createdSendSuccessfully" | i18n }}

- diff --git a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts index 24186ad4275..fdf147b360f 100644 --- a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts +++ b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts @@ -11,6 +11,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { ButtonModule, I18nMockService, IconModule, ToastService } from "@bitwarden/components"; @@ -50,6 +51,7 @@ describe("SendCreatedComponent", () => { sendView = { id: sendId, deletionDate: new Date(), + type: SendType.Text, accessId: "abc", urlB64Key: "123", } as SendView; @@ -129,9 +131,11 @@ describe("SendCreatedComponent", () => { expect(component["hoursAvailable"]).toBe(0); }); - it("should navigate back to send list on close", async () => { - await component.close(); - expect(router.navigate).toHaveBeenCalledWith(["/tabs/send"]); + it("should navigate back to the edit send form on close", async () => { + await component.goToEditSend(); + expect(router.navigate).toHaveBeenCalledWith(["/edit-send"], { + queryParams: { sendId: "test-send-id", type: SendType.Text }, + }); }); describe("getHoursAvailable", () => { diff --git a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.ts b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.ts index 88475d7dad9..98b09d380e4 100644 --- a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.ts @@ -77,7 +77,13 @@ export class SendCreatedComponent { return Math.max(0, Math.ceil((send.deletionDate.getTime() - now) / (1000 * 60 * 60))); } - async close() { + async goToEditSend() { + await this.router.navigate([`/edit-send`], { + queryParams: { sendId: this.send.id, type: this.send.type }, + }); + } + + async goBack() { await this.router.navigate(["/tabs/send"]); } From 116d2166c32926cd9fbd5785abb4f772243ae522 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:05:15 -0500 Subject: [PATCH 029/126] remove slideIn animation as it doesn't support the "show animations" setting (#11591) --- .../vault-generator-dialog.component.html | 2 +- .../vault-generator-dialog.component.ts | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.html index 7652b8ab0bf..72aaeea493d 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.html @@ -1,4 +1,4 @@ - + Date: Mon, 21 Oct 2024 11:50:50 -0700 Subject: [PATCH 030/126] fix voiceover on send created screen (#11628) --- .../popup/send-v2/send-created/send-created.component.html | 4 +++- .../components/send-details/send-details.component.html | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html index cdb514b8047..af3abbf5427 100644 --- a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html @@ -15,7 +15,9 @@ class="tw-flex tw-bg-background-alt tw-flex-col tw-justify-center tw-items-center tw-gap-2 tw-h-full tw-px-5" > -

{{ "createdSendSuccessfully" | i18n }}

+

+ {{ "createdSendSuccessfully" | i18n }} +

{{ formatExpirationDate() }}

diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html index d4c253303bd..93db4df3187 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html @@ -6,7 +6,7 @@

{{ "sendDetails" | i18n }}

{{ "name" | i18n }} - + Date: Mon, 21 Oct 2024 13:36:27 -0700 Subject: [PATCH 031/126] [PM-13809] - add remove password button (#11641) * add remove password button * adjust comment * use bitAction directive --- .../options/send-options.component.html | 59 +++++++++++-------- .../options/send-options.component.ts | 49 ++++++++++++++- 2 files changed, 82 insertions(+), 26 deletions(-) diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html index 265016ad1b1..98da24b5188 100644 --- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html @@ -12,33 +12,44 @@

{{ "additionalOptions" | i18n }}

> - {{ "password" | i18n }} - {{ "newPassword" | i18n }} + {{ "password" | i18n }} + + + + + - - {{ "sendPasswordDescV3" | i18n }} diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts index c8122dd4315..48ab78465c1 100644 --- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts @@ -7,14 +7,20 @@ import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { + AsyncActionsModule, + ButtonModule, CardComponent, CheckboxModule, + DialogService, FormFieldModule, IconButtonModule, SectionComponent, SectionHeaderComponent, + ToastService, TypographyModule, } from "@bitwarden/components"; import { CredentialGeneratorService, Generators } from "@bitwarden/generator-core"; @@ -27,6 +33,8 @@ import { SendFormContainer } from "../../send-form-container"; templateUrl: "./send-options.component.html", standalone: true, imports: [ + AsyncActionsModule, + ButtonModule, CardComponent, CheckboxModule, CommonModule, @@ -53,7 +61,7 @@ export class SendOptionsComponent implements OnInit { hideEmail: [false as boolean], }); - get shouldShowNewPassword(): boolean { + get hasPassword(): boolean { return this.originalSendView && this.originalSendView.password !== null; } @@ -71,8 +79,12 @@ export class SendOptionsComponent implements OnInit { constructor( private sendFormContainer: SendFormContainer, + private dialogService: DialogService, + private sendApiService: SendApiService, private formBuilder: FormBuilder, private policyService: PolicyService, + private i18nService: I18nService, + private toastService: ToastService, private generatorService: CredentialGeneratorService, ) { this.sendFormContainer.registerChildForm("sendOptionsForm", this.sendOptionsForm); @@ -110,16 +122,49 @@ export class SendOptionsComponent implements OnInit { }); }; + removePassword = async () => { + if (!this.originalSendView || !this.originalSendView.password) { + return; + } + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "removePassword" }, + content: { key: "removePasswordConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return false; + } + + await this.sendApiService.removePassword(this.originalSendView.id); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("removedPassword"), + }); + + this.originalSendView.password = null; + this.sendOptionsForm.patchValue({ + password: null, + }); + this.sendOptionsForm.get("password")?.enable(); + }; + ngOnInit() { if (this.sendFormContainer.originalSendView) { this.sendOptionsForm.patchValue({ maxAccessCount: this.sendFormContainer.originalSendView.maxAccessCount, accessCount: this.sendFormContainer.originalSendView.accessCount, - password: null, + password: this.hasPassword ? "************" : null, // 12 masked characters as a placeholder hideEmail: this.sendFormContainer.originalSendView.hideEmail, notes: this.sendFormContainer.originalSendView.notes, }); } + if (this.hasPassword) { + this.sendOptionsForm.get("password")?.disable(); + } + if (!this.config.areSendsAllowed) { this.sendOptionsForm.disable(); } From 79cdf3bf5031264340d4c263547d1092cbb8c2d1 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 20:53:30 +0000 Subject: [PATCH 032/126] Bumped client version(s) (#11648) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- 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 deb13794a54..00e09536616 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2024.10.2", + "version": "2024.10.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 c679767699a..184ab0c92de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -248,7 +248,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2024.10.2" + "version": "2024.10.3" }, "libs/admin-console": { "name": "@bitwarden/admin-console", From c16d1e0e74ef46c8020008707f9d29f812ac3c74 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:52:07 -0400 Subject: [PATCH 033/126] AnonLayoutWrapperComponents - Add reset support for null values (#11651) --- ...extension-anon-layout-wrapper.component.ts | 24 +++++++++++-------- .../anon-layout-wrapper.component.ts | 24 +++++++++++-------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts index 9d7644878d0..db85b28fa64 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts @@ -131,31 +131,35 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { return; } - if (data.pageTitle) { - this.pageTitle = this.handleStringOrTranslation(data.pageTitle); + // Null emissions are used to reset the page data as all fields are optional. + + if (data.pageTitle !== undefined) { + this.pageTitle = + data.pageTitle !== null ? this.handleStringOrTranslation(data.pageTitle) : null; } - if (data.pageSubtitle) { - this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle); + if (data.pageSubtitle !== undefined) { + this.pageSubtitle = + data.pageSubtitle !== null ? this.handleStringOrTranslation(data.pageSubtitle) : null; } - if (data.pageIcon) { - this.pageIcon = data.pageIcon; + if (data.pageIcon !== undefined) { + this.pageIcon = data.pageIcon !== null ? data.pageIcon : null; } - if (data.showReadonlyHostname != null) { + if (data.showReadonlyHostname !== undefined) { this.showReadonlyHostname = data.showReadonlyHostname; } - if (data.showAcctSwitcher != null) { + if (data.showAcctSwitcher !== undefined) { this.showAcctSwitcher = data.showAcctSwitcher; } - if (data.showBackButton != null) { + if (data.showBackButton !== undefined) { this.showBackButton = data.showBackButton; } - if (data.showLogo != null) { + if (data.showLogo !== undefined) { this.showLogo = data.showLogo; } } diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts index 27446335740..f805da0700a 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts @@ -14,17 +14,17 @@ export interface AnonLayoutWrapperData { * If a string is provided, it will be presented as is (ex: Organization name) * If a Translation object (supports placeholders) is provided, it will be translated */ - pageTitle?: string | Translation; + pageTitle?: string | Translation | null; /** * The optional subtitle of the page. * If a string is provided, it will be presented as is (ex: user's email) * If a Translation object (supports placeholders) is provided, it will be translated */ - pageSubtitle?: string | Translation; + pageSubtitle?: string | Translation | null; /** * The optional icon to display on the page. */ - pageIcon?: Icon; + pageIcon?: Icon | null; /** * Optional flag to either show the optional environment selector (false) or just a readonly hostname (true). */ @@ -114,19 +114,23 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { return; } - if (data.pageTitle) { - this.pageTitle = this.handleStringOrTranslation(data.pageTitle); + // Null emissions are used to reset the page data as all fields are optional. + + if (data.pageTitle !== undefined) { + this.pageTitle = + data.pageTitle !== null ? this.handleStringOrTranslation(data.pageTitle) : null; } - if (data.pageSubtitle) { - this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle); + if (data.pageSubtitle !== undefined) { + this.pageSubtitle = + data.pageSubtitle !== null ? this.handleStringOrTranslation(data.pageSubtitle) : null; } - if (data.pageIcon) { - this.pageIcon = data.pageIcon; + if (data.pageIcon !== undefined) { + this.pageIcon = data.pageIcon !== null ? data.pageIcon : null; } - if (data.showReadonlyHostname != null) { + if (data.showReadonlyHostname !== undefined) { this.showReadonlyHostname = data.showReadonlyHostname; } From 9a1879b96c4943563fbe5627202dd149b23be1aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 10:07:45 +0200 Subject: [PATCH 034/126] [deps] Tools: Update @types/papaparse to v5.3.15 (#11645) 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 184ab0c92de..05512349b51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -114,7 +114,7 @@ "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/node-ipc": "9.2.3", - "@types/papaparse": "5.3.14", + "@types/papaparse": "5.3.15", "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", "@types/zxcvbn": "4.4.5", @@ -9698,9 +9698,9 @@ } }, "node_modules/@types/papaparse": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz", - "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==", + "version": "5.3.15", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", + "integrity": "sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 38440adf92f..372da701a86 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/node-ipc": "9.2.3", - "@types/papaparse": "5.3.14", + "@types/papaparse": "5.3.15", "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", "@types/zxcvbn": "4.4.5", From 470ddf79ab1b2b4be402ebad8bf4fd560dc66fae Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Tue, 22 Oct 2024 08:46:45 -0400 Subject: [PATCH 035/126] [PM-12425] Remove FF: AC-2828_provider-portal-members-page (#11241) * Remove FF: AC-2828_provider-portal-members-page * Thomas' feedback: Fix provider layout --- ...tml => bulk-confirm-dialog.component.html} | 0 .../bulk/bulk-confirm-dialog.component.ts | 87 ++++++ .../components/bulk/bulk-confirm.component.ts | 132 --------- ...html => bulk-remove-dialog.component.html} | 0 .../bulk/bulk-remove-dialog.component.ts | 54 ++++ .../components/bulk/bulk-remove.component.ts | 76 ----- .../members/members.component.ts | 8 +- .../organizations/members/members.module.ts | 8 +- .../manage/bulk/bulk-confirm.component.ts | 37 --- .../manage/bulk/bulk-remove.component.ts | 24 -- .../dialogs/bulk-confirm-dialog.component.ts | 2 +- .../dialogs/bulk-remove-dialog.component.ts | 2 +- .../providers/manage/people.component.html | 203 ------------- .../providers/manage/people.component.ts | 267 ------------------ .../providers/providers-layout.component.html | 2 +- .../providers/providers-routing.module.ts | 27 +- .../providers/providers.module.ts | 6 - libs/common/src/enums/feature-flag.enum.ts | 4 +- 18 files changed, 164 insertions(+), 775 deletions(-) rename apps/web/src/app/admin-console/organizations/members/components/bulk/{bulk-confirm.component.html => bulk-confirm-dialog.component.html} (100%) create mode 100644 apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts delete mode 100644 apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts rename apps/web/src/app/admin-console/organizations/members/components/bulk/{bulk-remove.component.html => bulk-remove-dialog.component.html} (100%) create mode 100644 apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.ts delete mode 100644 apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-confirm.component.ts delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-remove.component.ts delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.html delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html rename to apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.html diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts new file mode 100644 index 00000000000..8e6ec1dbc34 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.ts @@ -0,0 +1,87 @@ +import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { firstValueFrom, map, Observable, switchMap } from "rxjs"; + +import { + OrganizationUserApiService, + OrganizationUserBulkConfirmRequest, + OrganizationUserBulkPublicKeyResponse, + OrganizationUserBulkResponse, +} from "@bitwarden/admin-console/common"; +import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response"; +import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { StateProvider } from "@bitwarden/common/platform/state"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; +import { DialogService } from "@bitwarden/components"; + +import { BaseBulkConfirmComponent } from "./base-bulk-confirm.component"; +import { BulkUserDetails } from "./bulk-status.component"; + +type BulkConfirmDialogParams = { + organizationId: string; + users: BulkUserDetails[]; +}; + +@Component({ + templateUrl: "bulk-confirm-dialog.component.html", +}) +export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent { + organizationId: string; + organizationKey$: Observable; + users: BulkUserDetails[]; + + constructor( + protected cryptoService: CryptoService, + @Inject(DIALOG_DATA) protected dialogParams: BulkConfirmDialogParams, + protected encryptService: EncryptService, + private organizationUserApiService: OrganizationUserApiService, + protected i18nService: I18nService, + private stateProvider: StateProvider, + ) { + super(cryptoService, encryptService, i18nService); + + this.organizationId = dialogParams.organizationId; + this.organizationKey$ = this.stateProvider.activeUserId$.pipe( + switchMap((userId) => this.cryptoService.orgKeys$(userId)), + map((organizationKeysById) => organizationKeysById[this.organizationId as OrganizationId]), + takeUntilDestroyed(), + ); + this.users = dialogParams.users; + } + + protected getCryptoKey = async (): Promise => + await firstValueFrom(this.organizationKey$); + + protected getPublicKeys = async (): Promise< + ListResponse + > => + await this.organizationUserApiService.postOrganizationUsersPublicKey( + this.organizationId, + this.filteredUsers.map((user) => user.id), + ); + + protected isAccepted = (user: BulkUserDetails) => + user.status === OrganizationUserStatusType.Accepted; + + protected postConfirmRequest = async ( + userIdsWithKeys: { id: string; key: string }[], + ): Promise> => { + const request = new OrganizationUserBulkConfirmRequest(userIdsWithKeys); + return await this.organizationUserApiService.postOrganizationUserBulkConfirm( + this.organizationId, + request, + ); + }; + + static open(dialogService: DialogService, config: DialogConfig) { + return dialogService.open(BulkConfirmDialogComponent, config); + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts deleted file mode 100644 index ee506840628..00000000000 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog"; -import { Component, Inject, OnInit } from "@angular/core"; - -import { - OrganizationUserApiService, - OrganizationUserBulkConfirmRequest, -} from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { DialogService } from "@bitwarden/components"; - -import { BulkUserDetails } from "./bulk-status.component"; - -type BulkConfirmDialogData = { - organizationId: string; - users: BulkUserDetails[]; -}; - -@Component({ - selector: "app-bulk-confirm", - templateUrl: "bulk-confirm.component.html", -}) -export class BulkConfirmComponent implements OnInit { - organizationId: string; - users: BulkUserDetails[]; - - excludedUsers: BulkUserDetails[]; - filteredUsers: BulkUserDetails[]; - publicKeys: Map = new Map(); - fingerprints: Map = new Map(); - statuses: Map = new Map(); - - loading = true; - done = false; - error: string; - - constructor( - @Inject(DIALOG_DATA) protected data: BulkConfirmDialogData, - protected cryptoService: CryptoService, - protected encryptService: EncryptService, - protected apiService: ApiService, - private organizationUserApiService: OrganizationUserApiService, - private i18nService: I18nService, - ) { - this.organizationId = data.organizationId; - this.users = data.users; - } - - async ngOnInit() { - this.excludedUsers = this.users.filter((u) => !this.isAccepted(u)); - this.filteredUsers = this.users.filter((u) => this.isAccepted(u)); - - if (this.filteredUsers.length <= 0) { - this.done = true; - } - - const response = await this.getPublicKeys(); - - for (const entry of response.data) { - const publicKey = Utils.fromB64ToArray(entry.key); - const fingerprint = await this.cryptoService.getFingerprint(entry.userId, publicKey); - if (fingerprint != null) { - this.publicKeys.set(entry.id, publicKey); - this.fingerprints.set(entry.id, fingerprint.join("-")); - } - } - - this.loading = false; - } - - async submit() { - this.loading = true; - try { - const key = await this.getCryptoKey(); - const userIdsWithKeys: any[] = []; - for (const user of this.filteredUsers) { - const publicKey = this.publicKeys.get(user.id); - if (publicKey == null) { - continue; - } - const encryptedKey = await this.encryptService.rsaEncrypt(key.key, publicKey); - userIdsWithKeys.push({ - id: user.id, - key: encryptedKey.encryptedString, - }); - } - const response = await this.postConfirmRequest(userIdsWithKeys); - - response.data.forEach((entry) => { - const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkConfirmMessage"); - this.statuses.set(entry.id, error); - }); - - this.done = true; - } catch (e) { - this.error = e.message; - } - this.loading = false; - } - - protected isAccepted(user: BulkUserDetails) { - return user.status === OrganizationUserStatusType.Accepted; - } - - protected async getPublicKeys() { - return await this.organizationUserApiService.postOrganizationUsersPublicKey( - this.organizationId, - this.filteredUsers.map((user) => user.id), - ); - } - - protected getCryptoKey(): Promise { - return this.cryptoService.getOrgKey(this.organizationId); - } - - protected async postConfirmRequest(userIdsWithKeys: any[]) { - const request = new OrganizationUserBulkConfirmRequest(userIdsWithKeys); - return await this.organizationUserApiService.postOrganizationUserBulkConfirm( - this.organizationId, - request, - ); - } - - static open(dialogService: DialogService, config: DialogConfig) { - return dialogService.open(BulkConfirmComponent, config); - } -} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html rename to apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.ts new file mode 100644 index 00000000000..9ff097debb0 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.ts @@ -0,0 +1,54 @@ +import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; + +import { + OrganizationUserApiService, + OrganizationUserBulkResponse, +} from "@bitwarden/admin-console/common"; +import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { DialogService } from "@bitwarden/components"; + +import { BaseBulkRemoveComponent } from "./base-bulk-remove.component"; +import { BulkUserDetails } from "./bulk-status.component"; + +type BulkRemoveDialogParams = { + organizationId: string; + users: BulkUserDetails[]; +}; + +@Component({ + templateUrl: "bulk-remove-dialog.component.html", +}) +export class BulkRemoveDialogComponent extends BaseBulkRemoveComponent { + organizationId: string; + users: BulkUserDetails[]; + + constructor( + @Inject(DIALOG_DATA) protected dialogParams: BulkRemoveDialogParams, + protected i18nService: I18nService, + private organizationUserApiService: OrganizationUserApiService, + ) { + super(i18nService); + this.organizationId = dialogParams.organizationId; + this.users = dialogParams.users; + this.showNoMasterPasswordWarning = this.users.some( + (u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false, + ); + } + + protected deleteUsers = (): Promise> => + this.organizationUserApiService.removeManyOrganizationUsers( + this.organizationId, + this.users.map((user) => user.id), + ); + + protected get removeUsersWarning() { + return this.i18nService.t("removeOrgUsersConfirmation"); + } + + static open(dialogService: DialogService, config: DialogConfig) { + return dialogService.open(BulkRemoveDialogComponent, config); + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts deleted file mode 100644 index 74939238fcc..00000000000 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog"; -import { Component, Inject } from "@angular/core"; - -import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { DialogService } from "@bitwarden/components"; - -import { BulkUserDetails } from "./bulk-status.component"; - -type BulkRemoveDialogData = { - organizationId: string; - users: BulkUserDetails[]; -}; - -@Component({ - selector: "app-bulk-remove", - templateUrl: "bulk-remove.component.html", -}) -export class BulkRemoveComponent { - organizationId: string; - users: BulkUserDetails[]; - - statuses: Map = new Map(); - - loading = false; - done = false; - error: string; - showNoMasterPasswordWarning = false; - - constructor( - @Inject(DIALOG_DATA) protected data: BulkRemoveDialogData, - protected apiService: ApiService, - protected i18nService: I18nService, - private organizationUserApiService: OrganizationUserApiService, - ) { - this.organizationId = data.organizationId; - this.users = data.users; - this.showNoMasterPasswordWarning = this.users.some( - (u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false, - ); - } - - submit = async () => { - this.loading = true; - try { - const response = await this.removeUsers(); - - response.data.forEach((entry) => { - const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkRemovedMessage"); - this.statuses.set(entry.id, error); - }); - this.done = true; - } catch (e) { - this.error = e.message; - } - - this.loading = false; - }; - - protected async removeUsers() { - return await this.organizationUserApiService.removeManyOrganizationUsers( - this.organizationId, - this.users.map((user) => user.id), - ); - } - - protected get removeUsersWarning() { - return this.i18nService.t("removeOrgUsersConfirmation"); - } - - static open(dialogService: DialogService, config: DialogConfig) { - return dialogService.open(BulkRemoveComponent, config); - } -} diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 3cc73c84a97..7ee99ff2e34 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -60,9 +60,9 @@ import { GroupService } from "../core"; import { OrganizationUserView } from "../core/views/organization-user.view"; import { openEntityEventsDialog } from "../manage/entity-events.component"; -import { BulkConfirmComponent } from "./components/bulk/bulk-confirm.component"; +import { BulkConfirmDialogComponent } from "./components/bulk/bulk-confirm-dialog.component"; import { BulkEnableSecretsManagerDialogComponent } from "./components/bulk/bulk-enable-sm-dialog.component"; -import { BulkRemoveComponent } from "./components/bulk/bulk-remove.component"; +import { BulkRemoveDialogComponent } from "./components/bulk/bulk-remove-dialog.component"; import { BulkRestoreRevokeComponent } from "./components/bulk/bulk-restore-revoke.component"; import { BulkStatusComponent } from "./components/bulk/bulk-status.component"; import { @@ -541,7 +541,7 @@ export class MembersComponent extends BaseMembersComponent return; } - const dialogRef = BulkRemoveComponent.open(this.dialogService, { + const dialogRef = BulkRemoveDialogComponent.open(this.dialogService, { data: { organizationId: this.organization.id, users: this.dataSource.getCheckedUsers(), @@ -620,7 +620,7 @@ export class MembersComponent extends BaseMembersComponent return; } - const dialogRef = BulkConfirmComponent.open(this.dialogService, { + const dialogRef = BulkConfirmDialogComponent.open(this.dialogService, { data: { organizationId: this.organization.id, users: this.dataSource.getCheckedUsers(), diff --git a/apps/web/src/app/admin-console/organizations/members/members.module.ts b/apps/web/src/app/admin-console/organizations/members/members.module.ts index d849b1f1f3c..d7c5a9bf1df 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.module.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.module.ts @@ -7,9 +7,9 @@ import { PasswordCalloutComponent } from "@bitwarden/auth/angular"; import { LooseComponentsModule } from "../../../shared"; import { SharedOrganizationModule } from "../shared"; -import { BulkConfirmComponent } from "./components/bulk/bulk-confirm.component"; +import { BulkConfirmDialogComponent } from "./components/bulk/bulk-confirm-dialog.component"; import { BulkEnableSecretsManagerDialogComponent } from "./components/bulk/bulk-enable-sm-dialog.component"; -import { BulkRemoveComponent } from "./components/bulk/bulk-remove.component"; +import { BulkRemoveDialogComponent } from "./components/bulk/bulk-remove-dialog.component"; import { BulkRestoreRevokeComponent } from "./components/bulk/bulk-restore-revoke.component"; import { BulkStatusComponent } from "./components/bulk/bulk-status.component"; import { UserDialogModule } from "./components/member-dialog"; @@ -28,9 +28,9 @@ import { MembersComponent } from "./members.component"; PasswordStrengthV2Component, ], declarations: [ - BulkConfirmComponent, + BulkConfirmDialogComponent, BulkEnableSecretsManagerDialogComponent, - BulkRemoveComponent, + BulkRemoveDialogComponent, BulkRestoreRevokeComponent, BulkStatusComponent, MembersComponent, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-confirm.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-confirm.component.ts deleted file mode 100644 index 918673e63f5..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-confirm.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, Input } from "@angular/core"; - -import { ProviderUserStatusType } from "@bitwarden/common/admin-console/enums"; -import { ProviderUserBulkConfirmRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk-confirm.request"; -import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-confirm.component"; -import { BulkUserDetails } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component"; - -/** - * @deprecated Please use the {@link BulkConfirmDialogComponent} instead. - */ -@Component({ - templateUrl: - "../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html", -}) -export class BulkConfirmComponent extends OrganizationBulkConfirmComponent { - @Input() providerId: string; - - protected override isAccepted(user: BulkUserDetails) { - return user.status === ProviderUserStatusType.Accepted; - } - - protected override async getPublicKeys() { - const request = new ProviderUserBulkRequest(this.filteredUsers.map((user) => user.id)); - return await this.apiService.postProviderUsersPublicKey(this.providerId, request); - } - - protected override getCryptoKey(): Promise { - return this.cryptoService.getProviderKey(this.providerId); - } - - protected override async postConfirmRequest(userIdsWithKeys: any[]) { - const request = new ProviderUserBulkConfirmRequest(userIdsWithKeys); - return await this.apiService.postProviderUserBulkConfirm(this.providerId, request); - } -} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-remove.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-remove.component.ts deleted file mode 100644 index ea3ea9b5967..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/bulk/bulk-remove.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, Input } from "@angular/core"; - -import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request"; -import { BulkRemoveComponent as OrganizationBulkRemoveComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-remove.component"; - -/** - * @deprecated Please use the {@link BulkRemoveDialogComponent} instead. - */ -@Component({ - templateUrl: - "../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html", -}) -export class BulkRemoveComponent extends OrganizationBulkRemoveComponent { - @Input() providerId: string; - - async deleteUsers() { - const request = new ProviderUserBulkRequest(this.users.map((user) => user.id)); - return await this.apiService.deleteManyProviderUsers(this.providerId, request); - } - - protected get removeUsersWarning() { - return this.i18nService.t("removeUsersWarning"); - } -} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts index 8a04cb6452d..61145efb783 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts @@ -27,7 +27,7 @@ type BulkConfirmDialogParams = { @Component({ templateUrl: - "../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html", + "../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm-dialog.component.html", }) export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent { providerId: string; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-remove-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-remove-dialog.component.ts index 16e64703700..b5d5274498c 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-remove-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-remove-dialog.component.ts @@ -17,7 +17,7 @@ type BulkRemoveDialogParams = { @Component({ templateUrl: - "../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html", + "../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html", }) export class BulkRemoveDialogComponent extends BaseBulkRemoveComponent { providerId: string; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.html deleted file mode 100644 index 36bc6543696..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - -
- - - {{ "all" | i18n }} - {{ allCount }} - - - - {{ "invited" | i18n }} - {{ invitedCount }} - - - - {{ "needsConfirmation" | i18n }} - {{ acceptedCount }} - - - - -
- - - - {{ "loading" | i18n }} - - -

{{ "noUsersInList" | i18n }}

- - - {{ "providerUsersNeedConfirmed" | i18n }} - - - - - - - - - - - - -
- - - - - {{ u.email }} - {{ - "invited" | i18n - }} - {{ - "needsConfirmation" | i18n - }} - {{ u.name }} - - - - {{ "userUsingTwoStep" | i18n }} - - - {{ "providerAdmin" | i18n }} - {{ "serviceUser" | i18n }} - - -
-
-
- - - - - diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts deleted file mode 100644 index 9293f8c6eb7..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts +++ /dev/null @@ -1,267 +0,0 @@ -import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; -import { ActivatedRoute, Router } from "@angular/router"; -import { lastValueFrom } from "rxjs"; -import { first } from "rxjs/operators"; - -import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; -import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; -import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service"; -import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; -import { ProviderUserStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums"; -import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request"; -import { ProviderUserConfirmRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-confirm.request"; -import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response"; -import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { BasePeopleComponent } from "@bitwarden/web-vault/app/admin-console/common/base.people.component"; -import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component"; -import { BulkStatusComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component"; - -import { BulkConfirmComponent } from "./bulk/bulk-confirm.component"; -import { BulkRemoveComponent } from "./bulk/bulk-remove.component"; -import { UserAddEditComponent } from "./user-add-edit.component"; - -/** - * @deprecated Please use the {@link MembersComponent} instead. - */ -@Component({ - selector: "provider-people", - templateUrl: "people.component.html", -}) -// eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class PeopleComponent - extends BasePeopleComponent - implements OnInit -{ - @ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef; - @ViewChild("groupsTemplate", { read: ViewContainerRef, static: true }) - groupsModalRef: ViewContainerRef; - @ViewChild("bulkStatusTemplate", { read: ViewContainerRef, static: true }) - bulkStatusModalRef: ViewContainerRef; - @ViewChild("bulkConfirmTemplate", { read: ViewContainerRef, static: true }) - bulkConfirmModalRef: ViewContainerRef; - @ViewChild("bulkRemoveTemplate", { read: ViewContainerRef, static: true }) - bulkRemoveModalRef: ViewContainerRef; - - userType = ProviderUserType; - userStatusType = ProviderUserStatusType; - status: ProviderUserStatusType = null; - providerId: string; - accessEvents = false; - - constructor( - apiService: ApiService, - private route: ActivatedRoute, - i18nService: I18nService, - modalService: ModalService, - platformUtilsService: PlatformUtilsService, - cryptoService: CryptoService, - private encryptService: EncryptService, - private router: Router, - searchService: SearchService, - validationService: ValidationService, - logService: LogService, - searchPipe: SearchPipe, - userNamePipe: UserNamePipe, - private providerService: ProviderService, - dialogService: DialogService, - organizationManagementPreferencesService: OrganizationManagementPreferencesService, - private configService: ConfigService, - protected toastService: ToastService, - ) { - super( - apiService, - searchService, - i18nService, - platformUtilsService, - cryptoService, - validationService, - modalService, - logService, - searchPipe, - userNamePipe, - dialogService, - organizationManagementPreferencesService, - toastService, - ); - } - - ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.parent.params.subscribe(async (params) => { - this.providerId = params.providerId; - const provider = await this.providerService.get(this.providerId); - - if (!provider.canManageUsers) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["../"], { relativeTo: this.route }); - return; - } - - this.accessEvents = provider.useEvents; - - await this.load(); - - /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ - this.route.queryParams.pipe(first()).subscribe(async (qParams) => { - this.searchControl.setValue(qParams.search); - if (qParams.viewEvents != null) { - const user = this.users.filter((u) => u.id === qParams.viewEvents); - if (user.length > 0 && user[0].status === ProviderUserStatusType.Confirmed) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.events(user[0]); - } - } - }); - }); - } - - getUsers(): Promise> { - return this.apiService.getProviderUsers(this.providerId); - } - - deleteUser(id: string): Promise { - return this.apiService.deleteProviderUser(this.providerId, id); - } - - revokeUser(id: string): Promise { - // Not implemented. - return null; - } - - restoreUser(id: string): Promise { - // Not implemented. - return null; - } - - reinviteUser(id: string): Promise { - return this.apiService.postProviderUserReinvite(this.providerId, id); - } - - async confirmUser(user: ProviderUserUserDetailsResponse, publicKey: Uint8Array): Promise { - const providerKey = await this.cryptoService.getProviderKey(this.providerId); - const key = await this.encryptService.rsaEncrypt(providerKey.key, publicKey); - const request = new ProviderUserConfirmRequest(); - request.key = key.encryptedString; - await this.apiService.postProviderUserConfirm(this.providerId, user.id, request); - } - - async edit(user: ProviderUserUserDetailsResponse) { - const [modal] = await this.modalService.openViewRef( - UserAddEditComponent, - this.addEditModalRef, - (comp) => { - comp.name = this.userNamePipe.transform(user); - comp.providerId = this.providerId; - comp.providerUserId = user != null ? user.id : null; - comp.savedUser.subscribe(() => { - modal.close(); - this.load(); - }); - comp.deletedUser.subscribe(() => { - modal.close(); - this.removeUser(user); - }); - }, - ); - } - - async events(user: ProviderUserUserDetailsResponse) { - await openEntityEventsDialog(this.dialogService, { - data: { - name: this.userNamePipe.transform(user), - providerId: this.providerId, - entityId: user.id, - showUser: false, - entity: "user", - }, - }); - } - - async bulkRemove() { - if (this.actionPromise != null) { - return; - } - - const [modal] = await this.modalService.openViewRef( - BulkRemoveComponent, - this.bulkRemoveModalRef, - (comp) => { - comp.providerId = this.providerId; - comp.users = this.getCheckedUsers(); - }, - ); - - await modal.onClosedPromise(); - await this.load(); - } - - async bulkReinvite() { - if (this.actionPromise != null) { - return; - } - - const users = this.getCheckedUsers(); - const filteredUsers = users.filter((u) => u.status === ProviderUserStatusType.Invited); - - if (filteredUsers.length <= 0) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("noSelectedUsersApplicable"), - }); - return; - } - - try { - const request = new ProviderUserBulkRequest(filteredUsers.map((user) => user.id)); - const response = this.apiService.postManyProviderUserReinvite(this.providerId, request); - // 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 - - // Bulk Status component open - const dialogRef = BulkStatusComponent.open(this.dialogService, { - data: { - users: users, - filteredUsers: filteredUsers, - request: response, - successfulMessage: this.i18nService.t("bulkReinviteMessage"), - }, - }); - await lastValueFrom(dialogRef.closed); - } catch (e) { - this.validationService.showError(e); - } - this.actionPromise = null; - } - - async bulkConfirm() { - if (this.actionPromise != null) { - return; - } - - const [modal] = await this.modalService.openViewRef( - BulkConfirmComponent, - this.bulkConfirmModalRef, - (comp) => { - comp.providerId = this.providerId; - comp.users = this.getCheckedUsers(); - }, - ); - - await modal.onClosedPromise(); - await this.load(); - } -} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html index 5f9b3f66bc5..0536221cafd 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html @@ -13,7 +13,7 @@ route="manage" *ngIf="showManageTab(provider)" > - + provider.canManageUsers), - ], - data: { - titleId: "people", - }, + { + path: "members", + component: MembersComponent, + canActivate: [ + providerPermissionsGuard((provider: Provider) => provider.canManageUsers), + ], + data: { + titleId: "members", }, - }), + }, { path: "events", component: EventsComponent, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index 8ed10a2d6e3..b6c7125c48c 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -24,14 +24,11 @@ import { AddOrganizationComponent } from "./clients/add-organization.component"; import { ClientsComponent } from "./clients/clients.component"; import { CreateOrganizationComponent } from "./clients/create-organization.component"; import { AcceptProviderComponent } from "./manage/accept-provider.component"; -import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component"; -import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component"; import { AddEditMemberDialogComponent } from "./manage/dialogs/add-edit-member-dialog.component"; import { BulkConfirmDialogComponent } from "./manage/dialogs/bulk-confirm-dialog.component"; import { BulkRemoveDialogComponent } from "./manage/dialogs/bulk-remove-dialog.component"; import { EventsComponent } from "./manage/events.component"; import { MembersComponent } from "./manage/members.component"; -import { PeopleComponent } from "./manage/people.component"; import { UserAddEditComponent } from "./manage/user-add-edit.component"; import { ProvidersLayoutComponent } from "./providers-layout.component"; import { ProvidersRoutingModule } from "./providers-routing.module"; @@ -58,14 +55,11 @@ import { SetupComponent } from "./setup/setup.component"; AcceptProviderComponent, AccountComponent, AddOrganizationComponent, - BulkConfirmComponent, BulkConfirmDialogComponent, - BulkRemoveComponent, BulkRemoveDialogComponent, ClientsComponent, CreateOrganizationComponent, EventsComponent, - PeopleComponent, MembersComponent, SetupComponent, SetupProviderComponent, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index fd3833d10e3..905d7299489 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -20,7 +20,7 @@ export enum FeatureFlag { EnableTimeThreshold = "PM-5864-dollar-threshold", InlineMenuPositioningImprovements = "inline-menu-positioning-improvements", ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner", - AC2828_ProviderPortalMembersPage = "AC-2828_provider-portal-members-page", + VaultBulkManagementAction = "vault-bulk-management-action", IdpAutoSubmitLogin = "idp-auto-submit-login", UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh", EnableUpgradePasswordManagerSub = "AC-2708-upgrade-password-manager-sub", @@ -67,7 +67,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.EnableTimeThreshold]: FALSE, [FeatureFlag.InlineMenuPositioningImprovements]: FALSE, [FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE, - [FeatureFlag.AC2828_ProviderPortalMembersPage]: FALSE, + [FeatureFlag.VaultBulkManagementAction]: FALSE, [FeatureFlag.IdpAutoSubmitLogin]: FALSE, [FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE, [FeatureFlag.EnableUpgradePasswordManagerSub]: FALSE, From 4a30782939012dbed73d9e659cf6461696af0dce Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Tue, 22 Oct 2024 15:15:15 +0200 Subject: [PATCH 036/126] [PM-12281] [PM-12301] [PM-12306] [PM-12334] Move delete item permission to Can Manage (#11289) * Added inputs to the view and edit component to disable or remove the delete button when a user does not have manage rights * Refactored editByCipherId to receive cipherview object * Fixed issue where adding an item on the individual vault throws a null reference * Fixed issue where adding an item on the AC vault throws a null reference * Allow delete in unassigned collection * created reusable service to check if a user has delete permission on an item * Registered service * Used authorizationservice on the browser and desktop Only display the delete button when a user has delete permission * Added comments to the service * Passed active collectionId to add edit component renamed constructor parameter * restored input property used by the web * Fixed dependency issue * Fixed dependency issue * Fixed dependency issue * Modified service to cater for org vault * Updated to include new dependency * Updated components to use the observable * Added check on the cli to know if user has rights to delete an item * Renamed abstraction and renamed implementation to include Default Fixed permission issues * Fixed test to reflect changes in implementation * Modified base classes to use new naming Passed new parameters for the canDeleteCipher * Modified base classes to use new naming Made changes from base class * Desktop changes Updated reference naming * cli changes Updated reference naming Passed new parameters for the canDeleteCipher$ * Updated references * browser changes Updated reference naming Passed new parameters for the canDeleteCipher$ * Modified cipher form dialog to take in active collection id used canDeleteCipher$ on the vault item dialog to disable the delete button when user does not have the required permissions * Fix number of arguments issue * Added active collection id * Updated canDeleteCipher$ arguments * Updated to pass the cipher object * Fixed up refrences and comments * Updated dependency * updated check to canEditUnassignedCiphers * Fixed unit tests * Removed activeCollectionId from cipher form * Fixed issue where bulk delete option shows for can edit users * Fix null reference when checking if a cipher belongs to the unassigned collection * Fixed bug where allowedCollection passed is undefined * Modified cipher by adding a isAdminConsoleAction argument to tell when a reuqest comes from the admin console * Passed isAdminConsoleAction as true when request is from the admin console --- .../browser/src/background/main.background.ts | 10 + .../vault-v2/view-v2/view-v2.component.html | 2 +- .../view-v2/view-v2.component.spec.ts | 7 + .../vault-v2/view-v2/view-v2.component.ts | 5 + .../components/vault/add-edit.component.html | 2 +- .../components/vault/add-edit.component.ts | 4 + .../components/vault/vault-items.component.ts | 2 +- .../components/vault/view.component.html | 2 +- .../popup/components/vault/view.component.ts | 18 +- apps/cli/src/oss-serve-configurator.ts | 1 + .../service-container/service-container.ts | 10 + apps/cli/src/vault.program.ts | 1 + apps/cli/src/vault/delete.command.ts | 10 + .../vault/app/vault/add-edit.component.html | 2 +- .../src/vault/app/vault/add-edit.component.ts | 3 + .../src/vault/app/vault/vault.component.html | 2 + .../src/vault/app/vault/view.component.html | 2 +- .../src/vault/app/vault/view.component.ts | 3 + .../emergency-add-edit-cipher.component.ts | 3 + .../vault-item-dialog.component.html | 2 +- .../vault-item-dialog.component.ts | 24 ++- .../vault-cipher-row.component.html | 2 +- .../vault-items/vault-cipher-row.component.ts | 1 + .../vault-items/vault-items.component.html | 8 +- .../vault-items/vault-items.component.ts | 93 +++++--- .../individual-vault/add-edit.component.html | 2 +- .../individual-vault/add-edit.component.ts | 3 + .../vault/individual-vault/vault.component.ts | 23 +- .../individual-vault/view.component.html | 2 +- .../individual-vault/view.component.spec.ts | 7 + .../vault/individual-vault/view.component.ts | 15 ++ .../app/vault/org-vault/add-edit.component.ts | 4 + .../app/vault/org-vault/vault.component.html | 1 + .../app/vault/org-vault/vault.component.ts | 11 +- .../src/services/jslib-services.module.ts | 9 + .../vault/components/add-edit.component.ts | 17 +- .../src/vault/components/view.component.ts | 10 +- .../cipher-authorization.service.spec.ts | 200 ++++++++++++++++++ .../services/cipher-authorization.service.ts | 86 ++++++++ 39 files changed, 551 insertions(+), 58 deletions(-) create mode 100644 libs/common/src/vault/services/cipher-authorization.service.spec.ts create mode 100644 libs/common/src/vault/services/cipher-authorization.service.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index a3dd1c473ae..e5a4087510c 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -177,6 +177,10 @@ import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitw import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + CipherAuthorizationService, + DefaultCipherAuthorizationService, +} from "@bitwarden/common/vault/services/cipher-authorization.service"; import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service"; import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; @@ -369,6 +373,7 @@ export default class MainBackground { themeStateService: DefaultThemeStateService; autoSubmitLoginBackground: AutoSubmitLoginBackground; sdkService: SdkService; + cipherAuthorizationService: CipherAuthorizationService; onUpdatedRan: boolean; onReplacedRan: boolean; @@ -1265,6 +1270,11 @@ export default class MainBackground { } this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.cryptoService); + + this.cipherAuthorizationService = new DefaultCipherAuthorizationService( + this.collectionService, + this.organizationService, + ); } async bootstrap() { diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html index a778d6aaea9..c2645f15ea8 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html @@ -28,7 +28,7 @@ -
+
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 38416c2c39c..ae2cf88fd1f 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 @@ -2,7 +2,7 @@ import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; import { CommonModule } from "@angular/common"; import { Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { Router } from "@angular/router"; -import { firstValueFrom, Subject } from "rxjs"; +import { firstValueFrom, Observable, Subject } from "rxjs"; import { map } from "rxjs/operators"; import { CollectionView } from "@bitwarden/admin-console/common"; @@ -12,12 +12,13 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { CipherId } from "@bitwarden/common/types/guid"; +import { CipherId, CollectionId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { AsyncActionsModule, ButtonModule, @@ -63,6 +64,16 @@ export interface VaultItemDialogParams { * If true, the "edit" button will be disabled in the dialog. */ disableForm?: boolean; + + /** + * The ID of the active collection. This is know the collection filter selected by the user. + */ + activeCollectionId?: CollectionId; + + /** + * If true, the dialog is being opened from the admin console. + */ + isAdminConsoleAction?: boolean; } export enum VaultItemDialogResult { @@ -204,6 +215,8 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { protected formConfig: CipherFormConfig = this.params.formConfig; + protected canDeleteCipher$: Observable; + constructor( @Inject(DIALOG_DATA) protected params: VaultItemDialogParams, private dialogRef: DialogRef, @@ -217,6 +230,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { private router: Router, private billingAccountProfileStateService: BillingAccountProfileStateService, private premiumUpgradeService: PremiumUpgradePromptService, + private cipherAuthorizationService: CipherAuthorizationService, ) { this.updateTitle(); } @@ -231,6 +245,12 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { this.organization = this.formConfig.organizations.find( (o) => o.id === this.cipher.organizationId, ); + + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$( + this.cipher, + [this.params.activeCollectionId], + this.params.isAdminConsoleAction, + ); } this.performingInitialLoad = false; diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html index 2f38d7c70db..286bbbab5ef 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html @@ -132,7 +132,7 @@ {{ "restore" | i18n }} - - diff --git a/apps/web/src/app/vault/individual-vault/view.component.spec.ts b/apps/web/src/app/vault/individual-vault/view.component.spec.ts index 0dd58b846d7..d1bfd221175 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.spec.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.spec.ts @@ -14,6 +14,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { ViewCipherDialogParams, ViewCipherDialogResult, ViewComponent } from "./view.component"; @@ -62,6 +63,12 @@ describe("ViewComponent", () => { useValue: mock(), }, { provide: ConfigService, useValue: mock() }, + { + provide: CipherAuthorizationService, + useValue: { + canDeleteCipher$: jest.fn().mockReturnValue(true), + }, + }, ], }).compileComponents(); diff --git a/apps/web/src/app/vault/individual-vault/view.component.ts b/apps/web/src/app/vault/individual-vault/view.component.ts index 99829e8f086..d30c453a4bd 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.ts @@ -1,6 +1,7 @@ import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; import { CommonModule } from "@angular/common"; import { Component, EventEmitter, Inject, OnInit } from "@angular/core"; +import { Observable } from "rxjs"; import { CollectionView } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -8,10 +9,12 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { AsyncActionsModule, DialogModule, @@ -34,6 +37,11 @@ export interface ViewCipherDialogParams { */ collections?: CollectionView[]; + /** + * Optional collection ID used to know the collection filter selected. + */ + activeCollectionId?: CollectionId; + /** * If true, the edit button will be disabled in the dialog. */ @@ -71,6 +79,8 @@ export class ViewComponent implements OnInit { cipherTypeString: string; organization: Organization; + canDeleteCipher$: Observable; + constructor( @Inject(DIALOG_DATA) public params: ViewCipherDialogParams, private dialogRef: DialogRef, @@ -81,6 +91,7 @@ export class ViewComponent implements OnInit { private cipherService: CipherService, private toastService: ToastService, private organizationService: OrganizationService, + private cipherAuthorizationService: CipherAuthorizationService, ) {} /** @@ -93,6 +104,10 @@ export class ViewComponent implements OnInit { if (this.cipher.organizationId) { this.organization = await this.organizationService.get(this.cipher.organizationId); } + + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher, [ + this.params.activeCollectionId, + ]); } /** diff --git a/apps/web/src/app/vault/org-vault/add-edit.component.ts b/apps/web/src/app/vault/org-vault/add-edit.component.ts index 9cb5542a7b7..7a4697f5af6 100644 --- a/apps/web/src/app/vault/org-vault/add-edit.component.ts +++ b/apps/web/src/app/vault/org-vault/add-edit.component.ts @@ -21,6 +21,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordRepromptService } from "@bitwarden/vault"; @@ -57,6 +58,7 @@ export class AddEditComponent extends BaseAddEditComponent { datePipe: DatePipe, configService: ConfigService, billingAccountProfileStateService: BillingAccountProfileStateService, + cipherAuthorizationService: CipherAuthorizationService, ) { super( cipherService, @@ -79,6 +81,7 @@ export class AddEditComponent extends BaseAddEditComponent { datePipe, configService, billingAccountProfileStateService, + cipherAuthorizationService, ); } @@ -90,6 +93,7 @@ export class AddEditComponent extends BaseAddEditComponent { } protected async loadCipher() { + this.isAdminConsoleAction = true; // Calling loadCipher first to assess if the cipher is unassigned. If null use apiService getCipherAdmin const firstCipherCheck = await super.loadCipher(); diff --git a/apps/web/src/app/vault/org-vault/vault.component.html b/apps/web/src/app/vault/org-vault/vault.component.html index 220d6ef490f..0bcdc52eaeb 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.html +++ b/apps/web/src/app/vault/org-vault/vault.component.html @@ -70,6 +70,7 @@ [viewingOrgVault]="true" [addAccessStatus]="addAccessStatus$ | async" [addAccessToggle]="showAddAccessToggle" + [activeCollection]="selectedCollection?.node" > diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index 94bb6011dc7..060ff7824d2 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -828,6 +828,7 @@ export class VaultComponent implements OnInit, OnDestroy { comp.organization = this.organization; comp.organizationId = this.organization.id; comp.cipherId = cipher?.id; + comp.collectionId = this.activeFilter.collectionId; comp.onSavedCipher.pipe(takeUntil(this.destroy$)).subscribe(() => { modal.close(); this.refresh(); @@ -897,7 +898,12 @@ export class VaultComponent implements OnInit, OnDestroy { cipher.type, ); - await this.openVaultItemDialog("view", cipherFormConfig, cipher); + await this.openVaultItemDialog( + "view", + cipherFormConfig, + cipher, + this.activeFilter.collectionId as CollectionId, + ); } /** @@ -907,6 +913,7 @@ export class VaultComponent implements OnInit, OnDestroy { mode: VaultItemDialogMode, formConfig: CipherFormConfig, cipher?: CipherView, + activeCollectionId?: CollectionId, ) { const disableForm = cipher ? !cipher.edit && !this.organization.canEditAllCiphers : false; // If the form is disabled, force the mode into `view` @@ -915,6 +922,8 @@ export class VaultComponent implements OnInit, OnDestroy { mode: dialogMode, formConfig, disableForm, + activeCollectionId, + isAdminConsoleAction: true, }); const result = await lastValueFrom(this.vaultItemDialogRef.closed); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 6af0fe2f660..e8d29bd69ba 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -242,6 +242,10 @@ import { } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service"; import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; +import { + CipherAuthorizationService, + DefaultCipherAuthorizationService, +} from "@bitwarden/common/vault/services/cipher-authorization.service"; import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service"; import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service"; @@ -1340,6 +1344,11 @@ const safeProviders: SafeProvider[] = [ ApiServiceAbstraction, ], }), + safeProvider({ + provide: CipherAuthorizationService, + useClass: DefaultCipherAuthorizationService, + deps: [CollectionService, OrganizationServiceAbstraction], + }), ]; @NgModule({ diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 49129a868be..44eaec03a68 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -23,7 +23,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { UserId } from "@bitwarden/common/types/guid"; +import { CollectionId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType, SecureNoteType } from "@bitwarden/common/vault/enums"; @@ -36,6 +36,7 @@ import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view" import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService } from "@bitwarden/components"; import { PasswordRepromptService } from "@bitwarden/vault"; @@ -47,6 +48,7 @@ export class AddEditComponent implements OnInit, OnDestroy { @Input() type: CipherType; @Input() collectionIds: string[]; @Input() organizationId: string = null; + @Input() collectionId: string = null; @Output() onSavedCipher = new EventEmitter(); @Output() onDeletedCipher = new EventEmitter(); @Output() onRestoredCipher = new EventEmitter(); @@ -57,6 +59,8 @@ export class AddEditComponent implements OnInit, OnDestroy { @Output() onGeneratePassword = new EventEmitter(); @Output() onGenerateUsername = new EventEmitter(); + canDeleteCipher$: Observable; + editMode = false; cipher: CipherView; folders$: Observable; @@ -83,6 +87,10 @@ export class AddEditComponent implements OnInit, OnDestroy { reprompt = false; canUseReprompt = true; organization: Organization; + /** + * Flag to determine if the action is being performed from the admin console. + */ + isAdminConsoleAction: boolean = false; protected componentName = ""; protected destroy$ = new Subject(); @@ -118,6 +126,7 @@ export class AddEditComponent implements OnInit, OnDestroy { protected win: Window, protected datePipe: DatePipe, protected configService: ConfigService, + protected cipherAuthorizationService: CipherAuthorizationService, ) { this.typeOptions = [ { name: i18nService.t("typeLogin"), value: CipherType.Login }, @@ -314,6 +323,12 @@ export class AddEditComponent implements OnInit, OnDestroy { if (this.reprompt) { this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[2].value; } + + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$( + this.cipher, + [this.collectionId as CollectionId], + this.isAdminConsoleAction, + ); } async submit(): Promise { diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index ac644acf9e4..4c96c10dac3 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -9,7 +9,7 @@ import { OnInit, Output, } from "@angular/core"; -import { firstValueFrom, map } from "rxjs"; +import { firstValueFrom, map, Observable } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; @@ -28,6 +28,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; +import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; @@ -37,6 +38,7 @@ import { Launchable } from "@bitwarden/common/vault/interfaces/launchable"; import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService } from "@bitwarden/components"; import { PasswordRepromptService } from "@bitwarden/vault"; @@ -45,12 +47,14 @@ const BroadcasterSubscriptionId = "ViewComponent"; @Directive() export class ViewComponent implements OnDestroy, OnInit { @Input() cipherId: string; + @Input() collectionId: string; @Output() onEditCipher = new EventEmitter(); @Output() onCloneCipher = new EventEmitter(); @Output() onShareCipher = new EventEmitter(); @Output() onDeletedCipher = new EventEmitter(); @Output() onRestoredCipher = new EventEmitter(); + canDeleteCipher$: Observable; cipher: CipherView; showPassword: boolean; showPasswordCount: boolean; @@ -105,6 +109,7 @@ export class ViewComponent implements OnDestroy, OnInit { protected datePipe: DatePipe, protected accountService: AccountService, private billingAccountProfileStateService: BillingAccountProfileStateService, + private cipherAuthorizationService: CipherAuthorizationService, ) {} ngOnInit() { @@ -144,6 +149,9 @@ export class ViewComponent implements OnDestroy, OnInit { ); this.showPremiumRequiredTotp = this.cipher.login.totp && !this.canAccessPremium && !this.cipher.organizationUseTotp; + this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(this.cipher, [ + this.collectionId as CollectionId, + ]); if (this.cipher.folderId) { this.folder = await ( diff --git a/libs/common/src/vault/services/cipher-authorization.service.spec.ts b/libs/common/src/vault/services/cipher-authorization.service.spec.ts new file mode 100644 index 00000000000..3155825d4d0 --- /dev/null +++ b/libs/common/src/vault/services/cipher-authorization.service.spec.ts @@ -0,0 +1,200 @@ +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { CollectionId } from "@bitwarden/common/types/guid"; + +import { CipherView } from "../models/view/cipher.view"; + +import { + CipherAuthorizationService, + DefaultCipherAuthorizationService, +} from "./cipher-authorization.service"; + +describe("CipherAuthorizationService", () => { + let cipherAuthorizationService: CipherAuthorizationService; + + const mockCollectionService = mock(); + const mockOrganizationService = mock(); + + // Mock factories + const createMockCipher = ( + organizationId: string | null, + collectionIds: string[], + edit: boolean = true, + ) => ({ + organizationId, + collectionIds, + edit, + }); + + const createMockCollection = (id: string, manage: boolean) => ({ + id, + manage, + }); + + const createMockOrganization = ({ + allowAdminAccessToAllCollectionItems = false, + canEditAllCiphers = false, + canEditUnassignedCiphers = false, + } = {}) => ({ + allowAdminAccessToAllCollectionItems, + canEditAllCiphers, + canEditUnassignedCiphers, + }); + + beforeEach(() => { + jest.clearAllMocks(); + cipherAuthorizationService = new DefaultCipherAuthorizationService( + mockCollectionService, + mockOrganizationService, + ); + }); + + describe("canDeleteCipher$", () => { + it("should return true if cipher has no organizationId", (done) => { + const cipher = createMockCipher(null, []) as CipherView; + + cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { + expect(result).toBe(true); + done(); + }); + }); + + it("should return true if isAdminConsoleAction is true and cipher is unassigned", (done) => { + const cipher = createMockCipher("org1", []) as CipherView; + const organization = createMockOrganization({ canEditUnassignedCiphers: true }); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + cipherAuthorizationService.canDeleteCipher$(cipher, [], true).subscribe((result) => { + expect(result).toBe(true); + done(); + }); + }); + + it("should return true if isAdminConsoleAction is true and user can edit all ciphers in the org", (done) => { + const cipher = createMockCipher("org1", ["col1"]) as CipherView; + const organization = createMockOrganization({ canEditAllCiphers: true }); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + cipherAuthorizationService.canDeleteCipher$(cipher, [], true).subscribe((result) => { + expect(result).toBe(true); + expect(mockOrganizationService.get$).toHaveBeenCalledWith("org1"); + done(); + }); + }); + + it("should return false if isAdminConsoleAction is true but user does not have permission to edit unassigned ciphers", (done) => { + const cipher = createMockCipher("org1", []) as CipherView; + const organization = createMockOrganization({ canEditUnassignedCiphers: false }); + mockOrganizationService.get$.mockReturnValue(of(organization as Organization)); + + cipherAuthorizationService.canDeleteCipher$(cipher, [], true).subscribe((result) => { + expect(result).toBe(false); + done(); + }); + }); + + it("should return true if activeCollectionId is provided and has manage permission", (done) => { + const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; + const activeCollectionId = "col1" as CollectionId; + const org = createMockOrganization(); + mockOrganizationService.get$.mockReturnValue(of(org as Organization)); + + const allCollections = [ + createMockCollection("col1", true), + createMockCollection("col2", false), + ]; + mockCollectionService.decryptedCollectionViews$.mockReturnValue( + of(allCollections as CollectionView[]), + ); + + cipherAuthorizationService + .canDeleteCipher$(cipher, [activeCollectionId]) + .subscribe((result) => { + expect(result).toBe(true); + expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ + "col1", + "col2", + ] as CollectionId[]); + done(); + }); + }); + + it("should return false if activeCollectionId is provided and manage permission is not present", (done) => { + const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; + const activeCollectionId = "col1" as CollectionId; + const org = createMockOrganization(); + mockOrganizationService.get$.mockReturnValue(of(org as Organization)); + + const allCollections = [ + createMockCollection("col1", false), + createMockCollection("col2", true), + ]; + mockCollectionService.decryptedCollectionViews$.mockReturnValue( + of(allCollections as CollectionView[]), + ); + + cipherAuthorizationService + .canDeleteCipher$(cipher, [activeCollectionId]) + .subscribe((result) => { + expect(result).toBe(false); + expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ + "col1", + "col2", + ] as CollectionId[]); + done(); + }); + }); + + it("should return true if any collection has manage permission", (done) => { + const cipher = createMockCipher("org1", ["col1", "col2", "col3"]) as CipherView; + const org = createMockOrganization(); + mockOrganizationService.get$.mockReturnValue(of(org as Organization)); + + const allCollections = [ + createMockCollection("col1", false), + createMockCollection("col2", true), + createMockCollection("col3", false), + ]; + mockCollectionService.decryptedCollectionViews$.mockReturnValue( + of(allCollections as CollectionView[]), + ); + + cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { + expect(result).toBe(true); + expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ + "col1", + "col2", + "col3", + ] as CollectionId[]); + done(); + }); + }); + + it("should return false if no collection has manage permission", (done) => { + const cipher = createMockCipher("org1", ["col1", "col2"]) as CipherView; + const org = createMockOrganization(); + mockOrganizationService.get$.mockReturnValue(of(org as Organization)); + + const allCollections = [ + createMockCollection("col1", false), + createMockCollection("col2", false), + ]; + mockCollectionService.decryptedCollectionViews$.mockReturnValue( + of(allCollections as CollectionView[]), + ); + + cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { + expect(result).toBe(false); + expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([ + "col1", + "col2", + ] as CollectionId[]); + done(); + }); + }); + }); +}); diff --git a/libs/common/src/vault/services/cipher-authorization.service.ts b/libs/common/src/vault/services/cipher-authorization.service.ts new file mode 100644 index 00000000000..00c7c412d61 --- /dev/null +++ b/libs/common/src/vault/services/cipher-authorization.service.ts @@ -0,0 +1,86 @@ +import { map, Observable, of, switchMap } from "rxjs"; + +import { CollectionService } from "@bitwarden/admin-console/common"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionId } from "@bitwarden/common/types/guid"; + +import { Cipher } from "../models/domain/cipher"; +import { CipherView } from "../models/view/cipher.view"; + +/** + * Represents either a cipher or a cipher view. + */ +type CipherLike = Cipher | CipherView; + +/** + * Service for managing user cipher authorization. + */ +export abstract class CipherAuthorizationService { + /** + * Determines if the user can delete the specified cipher. + * + * @param {CipherLike} cipher - The cipher object to evaluate for deletion permissions. + * @param {CollectionId[]} [allowedCollections] - Optional. The selected collection id from the vault filter. + * @param {boolean} isAdminConsoleAction - Optional. A flag indicating if the action is being performed from the admin console. + * + * @returns {Observable} - An observable that emits a boolean value indicating if the user can delete the cipher. + */ + canDeleteCipher$: ( + cipher: CipherLike, + allowedCollections?: CollectionId[], + isAdminConsoleAction?: boolean, + ) => Observable; +} + +/** + * {@link CipherAuthorizationService} + */ +export class DefaultCipherAuthorizationService implements CipherAuthorizationService { + constructor( + private collectionService: CollectionService, + private organizationService: OrganizationService, + ) {} + + /** + * + * {@link CipherAuthorizationService.canDeleteCipher$} + */ + canDeleteCipher$( + cipher: CipherLike, + allowedCollections?: CollectionId[], + isAdminConsoleAction?: boolean, + ): Observable { + if (cipher.organizationId == null) { + return of(true); + } + + return this.organizationService.get$(cipher.organizationId).pipe( + switchMap((organization) => { + if (isAdminConsoleAction) { + // If the user is an admin, they can delete an unassigned cipher + if (!cipher.collectionIds || cipher.collectionIds.length === 0) { + return of(organization?.canEditUnassignedCiphers === true); + } + + if (organization?.canEditAllCiphers) { + return of(true); + } + } + + return this.collectionService + .decryptedCollectionViews$(cipher.collectionIds as CollectionId[]) + .pipe( + map((allCollections) => { + const shouldFilter = allowedCollections?.some(Boolean); + + const collections = shouldFilter + ? allCollections.filter((c) => allowedCollections.includes(c.id as CollectionId)) + : allCollections; + + return collections.some((collection) => collection.manage); + }), + ); + }), + ); + } +} From 023abe2969068a7fd229e70e3a81d76fc4476b4d Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Tue, 22 Oct 2024 10:07:22 -0400 Subject: [PATCH 037/126] [PM-11199] added permission labels to ciphers in AC (#11210) * added permission labels to ciphers in AC --- .../vault-cipher-row.component.html | 6 ++- .../vault-items/vault-cipher-row.component.ts | 52 ++++++++++++++++++- .../app/vault/org-vault/vault.component.ts | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html index 286bbbab5ef..5c4de576ead 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html @@ -69,7 +69,11 @@ > - + +

+ {{ permissionText }} +

+
- + + >
{ + if (nav === "password") { + return this.i18nService.t("copyPassword"); + } + + if (nav === "passphrase") { + return this.i18nService.t("copyPassphrase"); + } + + return this.i18nService.t("copyUsername"); + }), + ); + + /** + * Emits the generate button aria-label respective of the selected credential type + * + * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. + */ + protected credentialTypeGenerateLabel$ = this.root$.pipe( + map(({ nav }) => { + if (nav === "password") { + return this.i18nService.t("generatePassword"); + } + + if (nav === "passphrase") { + return this.i18nService.t("generatePassphrase"); + } + + return this.i18nService.t("generateUsername"); + }), + ); + protected onRootChanged(nav: RootNavValue) { // prevent subscription cycle if (this.root$.value.nav !== nav) { diff --git a/libs/tools/generator/components/src/password-generator.component.html b/libs/tools/generator/components/src/password-generator.component.html index b4cf8c6cdb6..aecdf0f6a4d 100644 --- a/libs/tools/generator/components/src/password-generator.component.html +++ b/libs/tools/generator/components/src/password-generator.component.html @@ -14,18 +14,21 @@
- + + >
(null); + /** + * Emits the copy button aria-label respective of the selected credential + * + * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. + */ + protected credentialTypeCopyLabel$ = this.credentialType$.pipe( + map((cred) => { + if (cred === "password") { + return this.i18nService.t("copyPassword"); + } + + return this.i18nService.t("copyPassphrase"); + }), + ); + + /** + * Emits the generate button aria-label respective of the selected credential + * + * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. + */ + protected credentialTypeGenerateLabel$ = this.credentialType$.pipe( + map((cred) => { + if (cred === "password") { + return this.i18nService.t("generatePassword"); + } + + return this.i18nService.t("generatePassphrase"); + }), + ); + /** Emits the last generated value. */ protected readonly value$ = new BehaviorSubject(""); diff --git a/libs/tools/generator/components/src/username-generator.component.html b/libs/tools/generator/components/src/username-generator.component.html index e9d7d1c1f8c..ad8cd796123 100644 --- a/libs/tools/generator/components/src/username-generator.component.html +++ b/libs/tools/generator/components/src/username-generator.component.html @@ -3,18 +3,23 @@
- + + + + >
From 79d7d506df251e18dcbdc69e0c3aa59a85a0c28e Mon Sep 17 00:00:00 2001 From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:39:57 -0400 Subject: [PATCH 044/126] [PM-12996] Updating UI Spacing for bit section header (#11609) * Adding space to the section header * Updating spacing to the left of the bit section header --- .../vault-list-items-container.component.html | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html index bea6d9631ca..e89ec9472fb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html @@ -1,18 +1,20 @@ - -

- {{ title }} -

- - {{ ciphers.length }} -
+
+ +

+ {{ title }} +

+ + {{ ciphers.length }} +
+
{{ description }}
From e67577cc39a82ca20a77625898de527397a3ee91 Mon Sep 17 00:00:00 2001 From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:40:11 -0400 Subject: [PATCH 045/126] Updating chipSelect to be the new styling (#11593) --- .../vault-list-filters/vault-list-filters.component.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html index 0e241a81dcb..d9c4fbeee15 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html @@ -1,8 +1,12 @@
- + Date: Wed, 23 Oct 2024 12:11:42 -0400 Subject: [PATCH 046/126] [PM-8280] email forwarders (#11563) * forwarder lookup and generation support * localize algorithm names and descriptions in the credential generator service * add encryption support to UserStateSubject * move generic rx utilities to common * move icon button labels to generator configurations --- apps/browser/src/_locales/en/messages.json | 15 + apps/desktop/src/locales/en/messages.json | 18 + apps/web/src/locales/en/messages.json | 15 + libs/common/src/tools/dependencies.ts | 32 +- .../src/tools/integration/integration-id.ts | 14 +- libs/common/src/tools/private-classifier.ts | 31 + libs/common/src/tools/public-classifier.ts | 29 + libs/common/src/tools/rx.spec.ts | 496 +++++++++++++++- libs/common/src/tools/rx.ts | 125 +++- .../src/tools/state/classified-format.ts | 6 + .../tools/state/identity-state-constraint.ts | 26 +- libs/common/src/tools/state/object-key.ts | 53 ++ .../state/state-constraints-dependency.ts | 6 +- .../state/user-state-subject-dependencies.ts | 18 +- .../tools/state/user-state-subject.spec.ts | 315 +++++++--- .../src/tools/state/user-state-subject.ts | 408 +++++++++---- libs/common/src/tools/types.ts | 9 +- .../src/credential-generator.component.html | 27 +- .../src/credential-generator.component.ts | 370 ++++++++---- .../src/forwarder-settings.component.html | 16 + .../src/forwarder-settings.component.ts | 195 +++++++ .../components/src/generator.module.ts | 19 +- .../src/password-generator.component.ts | 54 +- .../src/username-generator.component.html | 23 +- .../src/username-generator.component.ts | 274 +++++++-- libs/tools/generator/components/src/util.ts | 2 +- .../core/src/data/generator-types.ts | 2 +- .../generator/core/src/data/generators.ts | 98 +++- .../generator/core/src/data/integrations.ts | 23 + .../src/engine/forwarder-configuration.ts | 35 +- .../generator/core/src/engine/forwarder.ts | 75 +++ .../generator/core/src/integration/addy-io.ts | 44 +- .../core/src/integration/duck-duck-go.ts | 42 +- .../core/src/integration/fastmail.ts | 46 +- .../core/src/integration/firefox-relay.ts | 42 +- .../core/src/integration/forward-email.ts | 41 +- .../core/src/integration/simple-login.ts | 42 +- libs/tools/generator/core/src/rx.spec.ts | 352 ----------- libs/tools/generator/core/src/rx.ts | 99 +--- .../credential-generator.service.spec.ts | 546 +++++++++++++++--- .../services/credential-generator.service.ts | 181 ++++-- .../credential-generator-configuration.ts | 78 ++- .../core/src/types/generator-type.ts | 31 +- libs/tools/generator/core/src/types/index.ts | 4 +- .../send-ui/src/send-form/send-form.module.ts | 13 +- 45 files changed, 3394 insertions(+), 996 deletions(-) create mode 100644 libs/common/src/tools/private-classifier.ts create mode 100644 libs/common/src/tools/public-classifier.ts create mode 100644 libs/common/src/tools/state/object-key.ts create mode 100644 libs/tools/generator/components/src/forwarder-settings.component.html create mode 100644 libs/tools/generator/components/src/forwarder-settings.component.ts create mode 100644 libs/tools/generator/core/src/engine/forwarder.ts delete mode 100644 libs/tools/generator/core/src/rx.spec.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 7fb21952ddf..e72daaa1717 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1395,6 +1395,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2833,6 +2837,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2873,6 +2880,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index f119d7366d6..e04941bdb91 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -835,6 +835,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1225,6 +1229,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2359,6 +2366,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2402,6 +2412,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index a27f13f9aee..07d94892adb 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6361,6 +6361,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6466,6 +6469,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8265,6 +8276,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, diff --git a/libs/common/src/tools/dependencies.ts b/libs/common/src/tools/dependencies.ts index 8b860591d54..84e2f53fa29 100644 --- a/libs/common/src/tools/dependencies.ts +++ b/libs/common/src/tools/dependencies.ts @@ -3,6 +3,8 @@ import { Observable } from "rxjs"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { UserId } from "@bitwarden/common/types/guid"; +import { UserEncryptor } from "./state/user-encryptor.abstraction"; + /** error emitted when the `SingleUserDependency` changes Ids */ export type UserChangedError = { /** the userId pinned by the single user dependency */ @@ -45,7 +47,35 @@ export type UserDependency = { userId$: Observable; }; -/** A pattern for types that depend upon a fixed userid and return +/** Decorates a type to indicate the user, if any, that the type is usable only by + * a specific user. + */ +export type UserBound = { [P in K]: T } & { + /** The user to which T is bound. */ + userId: UserId; +}; + +/** A pattern for types that depend upon a fixed-key encryptor and return + * an observable. + * + * Consumers of this dependency should emit a `UserChangedError` if + * the bound UserId changes or if the encryptor changes. If + * `singleUserEncryptor$` completes, the consumer should complete + * once all events received prior to the completion event are + * finished processing. The consumer should, where possible, + * prioritize these events in order to complete as soon as possible. + * If `singleUserEncryptor$` emits an unrecoverable error, the consumer + * should also emit the error. + */ +export type SingleUserEncryptorDependency = { + /** A stream that emits an encryptor when subscribed and the user key + * is available, and completes when the user key is no longer available. + * The stream should not emit null or undefined. + */ + singleUserEncryptor$: Observable>; +}; + +/** A pattern for types that depend upon a fixed-value userid and return * an observable. * * Consumers of this dependency should emit a `UserChangedError` if diff --git a/libs/common/src/tools/integration/integration-id.ts b/libs/common/src/tools/integration/integration-id.ts index 46b81c3c4c0..a15db143ee1 100644 --- a/libs/common/src/tools/integration/integration-id.ts +++ b/libs/common/src/tools/integration/integration-id.ts @@ -1,7 +1,13 @@ import { Opaque } from "type-fest"; +export const IntegrationIds = [ + "anonaddy", + "duckduckgo", + "fastmail", + "firefoxrelay", + "forwardemail", + "simplelogin", +] as const; + /** Identifies a vendor integrated into bitwarden */ -export type IntegrationId = Opaque< - "anonaddy" | "duckduckgo" | "fastmail" | "firefoxrelay" | "forwardemail" | "simplelogin", - "IntegrationId" ->; +export type IntegrationId = Opaque<(typeof IntegrationIds)[number], "IntegrationId">; diff --git a/libs/common/src/tools/private-classifier.ts b/libs/common/src/tools/private-classifier.ts new file mode 100644 index 00000000000..f9648504b76 --- /dev/null +++ b/libs/common/src/tools/private-classifier.ts @@ -0,0 +1,31 @@ +import { Jsonify } from "type-fest"; + +import { Classifier } from "@bitwarden/common/tools/state/classifier"; + +export class PrivateClassifier implements Classifier, Data> { + constructor(private keys: (keyof Jsonify)[] = undefined) {} + + classify(value: Data): { disclosed: Jsonify>; secret: Jsonify } { + const pickMe = JSON.parse(JSON.stringify(value)); + const keys: (keyof Jsonify)[] = this.keys ?? (Object.keys(pickMe) as any); + + const picked: Partial> = {}; + for (const key of keys) { + picked[key] = pickMe[key]; + } + const secret = picked as Jsonify; + + return { disclosed: null, secret }; + } + + declassify(_disclosed: Jsonify>, secret: Jsonify) { + const result: Partial> = {}; + const keys: (keyof Jsonify)[] = this.keys ?? (Object.keys(secret) as any); + + for (const key of keys) { + result[key] = secret[key]; + } + + return result as Jsonify; + } +} diff --git a/libs/common/src/tools/public-classifier.ts b/libs/common/src/tools/public-classifier.ts new file mode 100644 index 00000000000..82396f1c169 --- /dev/null +++ b/libs/common/src/tools/public-classifier.ts @@ -0,0 +1,29 @@ +import { Jsonify } from "type-fest"; + +import { Classifier } from "@bitwarden/common/tools/state/classifier"; + +export class PublicClassifier implements Classifier> { + constructor(private keys: (keyof Jsonify)[]) {} + + classify(value: Data): { disclosed: Jsonify; secret: Jsonify> } { + const pickMe = JSON.parse(JSON.stringify(value)); + + const picked: Partial> = {}; + for (const key of this.keys) { + picked[key] = pickMe[key]; + } + const disclosed = picked as Jsonify; + + return { disclosed, secret: null }; + } + + declassify(disclosed: Jsonify, _secret: Jsonify>) { + const result: Partial> = {}; + + for (const key of this.keys) { + result[key] = disclosed[key]; + } + + return result as Jsonify; + } +} diff --git a/libs/common/src/tools/rx.spec.ts b/libs/common/src/tools/rx.spec.ts index 8a2c1e38f5c..f6932f01dc1 100644 --- a/libs/common/src/tools/rx.spec.ts +++ b/libs/common/src/tools/rx.spec.ts @@ -2,11 +2,18 @@ * include structuredClone in test environment. * @jest-environment ../../../../shared/test.environment.ts */ -import { of, firstValueFrom } from "rxjs"; +import { of, firstValueFrom, Subject, tap, EmptyError } from "rxjs"; import { awaitAsync, trackEmissions } from "../../spec"; -import { distinctIfShallowMatch, reduceCollection } from "./rx"; +import { + anyComplete, + distinctIfShallowMatch, + on, + ready, + reduceCollection, + withLatestReady, +} from "./rx"; describe("reduceCollection", () => { it.each([[null], [undefined], [[]]])( @@ -84,3 +91,488 @@ describe("distinctIfShallowMatch", () => { expect(result).toEqual([{ foo: true, bar: true }]); }); }); + +describe("anyComplete", () => { + it("emits true when its input completes", () => { + const input$ = new Subject(); + + const emissions: boolean[] = []; + anyComplete(input$).subscribe((e) => emissions.push(e)); + input$.complete(); + + expect(emissions).toEqual([true]); + }); + + it("completes when its input is already complete", () => { + const input = new Subject(); + input.complete(); + + let completed = false; + anyComplete(input).subscribe({ complete: () => (completed = true) }); + + expect(completed).toBe(true); + }); + + it("completes when any input completes", () => { + const input$ = new Subject(); + const completing$ = new Subject(); + + let completed = false; + anyComplete([input$, completing$]).subscribe({ complete: () => (completed = true) }); + completing$.complete(); + + expect(completed).toBe(true); + }); + + it("ignores emissions", () => { + const input$ = new Subject(); + + const emissions: boolean[] = []; + anyComplete(input$).subscribe((e) => emissions.push(e)); + input$.next(1); + input$.next(2); + input$.complete(); + + expect(emissions).toEqual([true]); + }); + + it("forwards errors", () => { + const input$ = new Subject(); + const expected = { some: "error" }; + + let error = null; + anyComplete(input$).subscribe({ error: (e: unknown) => (error = e) }); + input$.error(expected); + + expect(error).toEqual(expected); + }); +}); + +describe("ready", () => { + it("connects when subscribed", () => { + const watch$ = new Subject(); + let connected = false; + const source$ = new Subject().pipe(tap({ subscribe: () => (connected = true) })); + + // precondition: ready$ should be cold + const ready$ = source$.pipe(ready(watch$)); + expect(connected).toBe(false); + + ready$.subscribe(); + + expect(connected).toBe(true); + }); + + it("suppresses source emissions until its watch emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + const results: number[] = []; + ready$.subscribe((n) => results.push(n)); + + // precondition: no emissions + source$.next(1); + expect(results).toEqual([]); + + watch$.next(); + + expect(results).toEqual([1]); + }); + + it("suppresses source emissions until all watches emit", () => { + const watchA$ = new Subject(); + const watchB$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready([watchA$, watchB$])); + const results: number[] = []; + ready$.subscribe((n) => results.push(n)); + + // preconditions: no emissions + source$.next(1); + expect(results).toEqual([]); + watchA$.next(); + expect(results).toEqual([]); + + watchB$.next(); + + expect(results).toEqual([1]); + }); + + it("emits the last source emission when its watch emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + const results: number[] = []; + ready$.subscribe((n) => results.push(n)); + + // precondition: no emissions + source$.next(1); + expect(results).toEqual([]); + + source$.next(2); + watch$.next(); + + expect(results).toEqual([2]); + }); + + it("emits all source emissions after its watch emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + const results: number[] = []; + ready$.subscribe((n) => results.push(n)); + + watch$.next(); + source$.next(1); + source$.next(2); + + expect(results).toEqual([1, 2]); + }); + + it("ignores repeated watch emissions", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + const results: number[] = []; + ready$.subscribe((n) => results.push(n)); + + watch$.next(); + source$.next(1); + watch$.next(); + source$.next(2); + watch$.next(); + + expect(results).toEqual([1, 2]); + }); + + it("completes when its source completes", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + let completed = false; + ready$.subscribe({ complete: () => (completed = true) }); + + source$.complete(); + + expect(completed).toBeTruthy(); + }); + + it("errors when its source errors", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + const expected = { some: "error" }; + let error = null; + ready$.subscribe({ error: (e: unknown) => (error = e) }); + + source$.error(expected); + + expect(error).toEqual(expected); + }); + + it("errors when its watch errors", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + const expected = { some: "error" }; + let error = null; + ready$.subscribe({ error: (e: unknown) => (error = e) }); + + watch$.error(expected); + + expect(error).toEqual(expected); + }); + + it("errors when its watch completes before emitting", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(ready(watch$)); + let error = null; + ready$.subscribe({ error: (e: unknown) => (error = e) }); + + watch$.complete(); + + expect(error).toBeInstanceOf(EmptyError); + }); +}); + +describe("withLatestReady", () => { + it("connects when subscribed", () => { + const watch$ = new Subject(); + let connected = false; + const source$ = new Subject().pipe(tap({ subscribe: () => (connected = true) })); + + // precondition: ready$ should be cold + const ready$ = source$.pipe(withLatestReady(watch$)); + expect(connected).toBe(false); + + ready$.subscribe(); + + expect(connected).toBe(true); + }); + + it("suppresses source emissions until its watch emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + const results: [number, string][] = []; + ready$.subscribe((n) => results.push(n)); + + // precondition: no emissions + source$.next(1); + expect(results).toEqual([]); + + watch$.next("watch"); + + expect(results).toEqual([[1, "watch"]]); + }); + + it("emits the last source emission when its watch emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + const results: [number, string][] = []; + ready$.subscribe((n) => results.push(n)); + + // precondition: no emissions + source$.next(1); + expect(results).toEqual([]); + + source$.next(2); + watch$.next("watch"); + + expect(results).toEqual([[2, "watch"]]); + }); + + it("emits all source emissions after its watch emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + const results: [number, string][] = []; + ready$.subscribe((n) => results.push(n)); + + watch$.next("watch"); + source$.next(1); + source$.next(2); + + expect(results).toEqual([ + [1, "watch"], + [2, "watch"], + ]); + }); + + it("appends the latest watch emission", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + const results: [number, string][] = []; + ready$.subscribe((n) => results.push(n)); + + watch$.next("ignored"); + watch$.next("watch"); + source$.next(1); + watch$.next("ignored"); + watch$.next("watch"); + source$.next(2); + + expect(results).toEqual([ + [1, "watch"], + [2, "watch"], + ]); + }); + + it("completes when its source completes", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + let completed = false; + ready$.subscribe({ complete: () => (completed = true) }); + + source$.complete(); + + expect(completed).toBeTruthy(); + }); + + it("errors when its source errors", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + const expected = { some: "error" }; + let error = null; + ready$.subscribe({ error: (e: unknown) => (error = e) }); + + source$.error(expected); + + expect(error).toEqual(expected); + }); + + it("errors when its watch errors", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + const expected = { some: "error" }; + let error = null; + ready$.subscribe({ error: (e: unknown) => (error = e) }); + + watch$.error(expected); + + expect(error).toEqual(expected); + }); + + it("errors when its watch completes before emitting", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const ready$ = source$.pipe(withLatestReady(watch$)); + let error = null; + ready$.subscribe({ error: (e: unknown) => (error = e) }); + + watch$.complete(); + + expect(error).toBeInstanceOf(EmptyError); + }); +}); + +describe("on", () => { + it("connects when subscribed", () => { + const watch$ = new Subject(); + let connected = false; + const source$ = new Subject().pipe(tap({ subscribe: () => (connected = true) })); + + // precondition: on$ should be cold + const on$ = source$.pipe(on(watch$)); + expect(connected).toBeFalsy(); + + on$.subscribe(); + + expect(connected).toBeTruthy(); + }); + + it("suppresses source emissions until `on` emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(on(watch$)).subscribe((n) => results.push(n)); + + // precondition: on$ should be cold + source$.next(1); + expect(results).toEqual([]); + + watch$.next(); + + expect(results).toEqual([1]); + }); + + it("repeats source emissions when `on` emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(on(watch$)).subscribe((n) => results.push(n)); + source$.next(1); + + watch$.next(); + watch$.next(); + + expect(results).toEqual([1, 1]); + }); + + it("updates source emissions when `on` emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(on(watch$)).subscribe((n) => results.push(n)); + + source$.next(1); + watch$.next(); + source$.next(2); + watch$.next(); + + expect(results).toEqual([1, 2]); + }); + + it("emits a value when `on` emits before the source is ready", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(on(watch$)).subscribe((n) => results.push(n)); + + watch$.next(); + source$.next(1); + + expect(results).toEqual([1]); + }); + + it("ignores repeated `on` emissions before the source is ready", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(on(watch$)).subscribe((n) => results.push(n)); + + watch$.next(); + watch$.next(); + source$.next(1); + + expect(results).toEqual([1]); + }); + + it("emits only the latest source emission when `on` emits", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(on(watch$)).subscribe((n) => results.push(n)); + source$.next(1); + + watch$.next(); + + source$.next(2); + source$.next(3); + watch$.next(); + + expect(results).toEqual([1, 3]); + }); + + it("completes when its source completes", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + let complete: boolean = false; + source$.pipe(on(watch$)).subscribe({ complete: () => (complete = true) }); + + source$.complete(); + + expect(complete).toBeTruthy(); + }); + + it("completes when its watch completes", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + let complete: boolean = false; + source$.pipe(on(watch$)).subscribe({ complete: () => (complete = true) }); + + watch$.complete(); + + expect(complete).toBeTruthy(); + }); + + it("errors when its source errors", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const expected = { some: "error" }; + let error = null; + source$.pipe(on(watch$)).subscribe({ error: (e: unknown) => (error = e) }); + + source$.error(expected); + + expect(error).toEqual(expected); + }); + + it("errors when its watch errors", () => { + const watch$ = new Subject(); + const source$ = new Subject(); + const expected = { some: "error" }; + let error = null; + source$.pipe(on(watch$)).subscribe({ error: (e: unknown) => (error = e) }); + + watch$.error(expected); + + expect(error).toEqual(expected); + }); +}); diff --git a/libs/common/src/tools/rx.ts b/libs/common/src/tools/rx.ts index d2c5747a882..d5d0b499ff2 100644 --- a/libs/common/src/tools/rx.ts +++ b/libs/common/src/tools/rx.ts @@ -1,4 +1,21 @@ -import { map, distinctUntilChanged, OperatorFunction } from "rxjs"; +import { + map, + distinctUntilChanged, + OperatorFunction, + Observable, + ignoreElements, + endWith, + race, + pipe, + connect, + ReplaySubject, + concat, + zip, + first, + takeUntil, + withLatestFrom, + concatMap, +} from "rxjs"; /** * An observable operator that reduces an emitted collection to a single object, @@ -36,3 +53,109 @@ export function distinctIfShallowMatch(): OperatorFunction { return isDistinct; }); } + +/** Create an observable that, once subscribed, emits `true` then completes when + * any input completes. If an input is already complete when the subscription + * occurs, it emits immediately. + * @param watch$ the observable(s) to watch for completion; if an array is passed, + * null and undefined members are ignored. If `watch$` is empty, `anyComplete` + * will never complete. + * @returns An observable that emits `true` when any of its inputs + * complete. The observable forwards the first error from its input. + * @remarks This method is particularly useful in combination with `takeUntil` and + * streams that are not guaranteed to complete on their own. + */ +export function anyComplete(watch$: Observable | Observable[]): Observable { + if (Array.isArray(watch$)) { + const completes$ = watch$ + .filter((w$) => !!w$) + .map((w$) => w$.pipe(ignoreElements(), endWith(true))); + const completed$ = race(completes$); + return completed$; + } else { + return watch$.pipe(ignoreElements(), endWith(true)); + } +} + +/** + * Create an observable that delays the input stream until all watches have + * emitted a value. The watched values are not included in the source stream. + * The last emission from the source is output when all the watches have + * emitted at least once. + * @param watch$ the observable(s) to watch for readiness. If `watch$` is empty, + * `ready` will never emit. + * @returns An observable that emits when the source stream emits. The observable + * errors if one of its watches completes before emitting. It also errors if one + * of its watches errors. + */ +export function ready(watch$: Observable | Observable[]) { + const watching$ = Array.isArray(watch$) ? watch$ : [watch$]; + return pipe( + connect>((source$) => { + // this subscription is safe because `source$` connects only after there + // is an external subscriber. + const source = new ReplaySubject(1); + source$.subscribe(source); + + // `concat` is subscribed immediately after it's returned, at which point + // `zip` blocks until all items in `watching$` are ready. If that occurs + // after `source$` is hot, then the replay subject sends the last-captured + // emission through immediately. Otherwise, `ready` waits for the next + // emission + return concat(zip(watching$).pipe(first(), ignoreElements()), source).pipe( + takeUntil(anyComplete(source)), + ); + }), + ); +} + +export function withLatestReady( + watch$: Observable, +): OperatorFunction { + return connect((source$) => { + // these subscriptions are safe because `source$` connects only after there + // is an external subscriber. + const source = new ReplaySubject(1); + source$.subscribe(source); + const watch = new ReplaySubject(1); + watch$.subscribe(watch); + + // `concat` is subscribed immediately after it's returned, at which point + // `zip` blocks until all items in `watching$` are ready. If that occurs + // after `source$` is hot, then the replay subject sends the last-captured + // emission through immediately. Otherwise, `ready` waits for the next + // emission + return concat(zip(watch).pipe(first(), ignoreElements()), source).pipe( + withLatestFrom(watch), + takeUntil(anyComplete(source)), + ); + }); +} + +/** + * Create an observable that emits the latest value of the source stream + * when `watch$` emits. If `watch$` emits before the stream emits, then + * an emission occurs as soon as a value becomes ready. + * @param watch$ the observable that triggers emissions + * @returns An observable that emits when `watch$` emits. The observable + * errors if its source stream errors. It also errors if `on` errors. It + * completes if its watch completes. + * + * @remarks This works like `audit`, but it repeats emissions when + * watch$ fires. + */ +export function on(watch$: Observable) { + return pipe( + connect>((source$) => { + const source = new ReplaySubject(1); + source$.subscribe(source); + + return watch$ + .pipe( + ready(source), + concatMap(() => source.pipe(first())), + ) + .pipe(takeUntil(anyComplete(source))); + }), + ); +} diff --git a/libs/common/src/tools/state/classified-format.ts b/libs/common/src/tools/state/classified-format.ts index 93147a0fb53..26aca0197c5 100644 --- a/libs/common/src/tools/state/classified-format.ts +++ b/libs/common/src/tools/state/classified-format.ts @@ -17,3 +17,9 @@ export type ClassifiedFormat = { */ readonly disclosed: Jsonify; }; + +export function isClassifiedFormat( + value: any, +): value is ClassifiedFormat { + return "id" in value && "secret" in value && "disclosed" in value; +} diff --git a/libs/common/src/tools/state/identity-state-constraint.ts b/libs/common/src/tools/state/identity-state-constraint.ts index ff7712b9091..df33dad543a 100644 --- a/libs/common/src/tools/state/identity-state-constraint.ts +++ b/libs/common/src/tools/state/identity-state-constraint.ts @@ -1,4 +1,11 @@ -import { Constraints, StateConstraints } from "../types"; +import { BehaviorSubject, Observable } from "rxjs"; + +import { + Constraints, + DynamicStateConstraints, + StateConstraints, + SubjectConstraints, +} from "../types"; // The constraints type shares the properties of the state, // but never has any members @@ -9,16 +16,31 @@ const EMPTY_CONSTRAINTS = new Proxy(Object.freeze({}), { }); /** A constraint that does nothing. */ -export class IdentityConstraint implements StateConstraints { +export class IdentityConstraint + implements StateConstraints, DynamicStateConstraints +{ /** Instantiate the identity constraint */ constructor() {} readonly constraints: Readonly> = EMPTY_CONSTRAINTS; + calibrate() { + return this; + } + adjust(state: State) { return state; } + fix(state: State) { return state; } } + +/** Emits a constraint that does not alter the input state. */ +export function unconstrained$(): Observable> { + const identity = new IdentityConstraint(); + const constraints$ = new BehaviorSubject(identity); + + return constraints$; +} diff --git a/libs/common/src/tools/state/object-key.ts b/libs/common/src/tools/state/object-key.ts new file mode 100644 index 00000000000..88365d5cbd1 --- /dev/null +++ b/libs/common/src/tools/state/object-key.ts @@ -0,0 +1,53 @@ +import { UserKeyDefinition, UserKeyDefinitionOptions } from "../../platform/state"; +// eslint-disable-next-line -- `StateDefinition` used as a type +import type { StateDefinition } from "../../platform/state/state-definition"; + +import { ClassifiedFormat } from "./classified-format"; +import { Classifier } from "./classifier"; + +/** A key for storing JavaScript objects (`{ an: "example" }`) + * in a UserStateSubject. + */ +// FIXME: promote to class: `ObjectConfiguration`. +// The class receives `encryptor`, `prepareNext`, `adjust`, and `fix` +// From `UserStateSubject`. `UserStateSubject` keeps `classify` and +// `declassify`. The class should also include serialization +// facilities (to be used in place of JSON.parse/stringify) in it's +// options. Also allow swap between "classifier" and "classification"; the +// latter is a list of properties/arguments to the specific classifier in-use. +export type ObjectKey> = { + target: "object"; + key: string; + state: StateDefinition; + classifier: Classifier; + format: "plain" | "classified"; + options: UserKeyDefinitionOptions; +}; + +export function isObjectKey(key: any): key is ObjectKey { + return key.target === "object" && "format" in key && "classifier" in key; +} + +export function toUserKeyDefinition( + key: ObjectKey, +) { + if (key.format === "plain") { + const plain = new UserKeyDefinition(key.state, key.key, key.options); + + return plain; + } else if (key.format === "classified") { + const classified = new UserKeyDefinition>( + key.state, + key.key, + { + cleanupDelayMs: key.options.cleanupDelayMs, + deserializer: (jsonValue) => jsonValue as ClassifiedFormat, + clearOn: key.options.clearOn, + }, + ); + + return classified; + } else { + throw new Error(`unknown format: ${key.format}`); + } +} diff --git a/libs/common/src/tools/state/state-constraints-dependency.ts b/libs/common/src/tools/state/state-constraints-dependency.ts index 66bac636bd7..427ff42e7a4 100644 --- a/libs/common/src/tools/state/state-constraints-dependency.ts +++ b/libs/common/src/tools/state/state-constraints-dependency.ts @@ -1,6 +1,6 @@ import { Observable } from "rxjs"; -import { DynamicStateConstraints, StateConstraints } from "../types"; +import { DynamicStateConstraints, StateConstraints, SubjectConstraints } from "../types"; /** A pattern for types that depend upon a dynamic set of constraints. * @@ -10,12 +10,12 @@ import { DynamicStateConstraints, StateConstraints } from "../types"; * last-emitted constraints. If `constraints$` completes, the consumer should * continue using the last-emitted constraints. */ -export type StateConstraintsDependency = { +export type SubjectConstraintsDependency = { /** A stream that emits constraints when subscribed and when the * constraints change. The stream should not emit `null` or * `undefined`. */ - constraints$: Observable | DynamicStateConstraints>; + constraints$: Observable>; }; /** Returns `true` if the input constraint is a `DynamicStateConstraints`. diff --git a/libs/common/src/tools/state/user-state-subject-dependencies.ts b/libs/common/src/tools/state/user-state-subject-dependencies.ts index 7f36ab7cae8..0ba842334bf 100644 --- a/libs/common/src/tools/state/user-state-subject-dependencies.ts +++ b/libs/common/src/tools/state/user-state-subject-dependencies.ts @@ -1,15 +1,23 @@ -import { Simplify } from "type-fest"; +import { RequireExactlyOne, Simplify } from "type-fest"; -import { Dependencies, SingleUserDependency, WhenDependency } from "../dependencies"; +import { + Dependencies, + SingleUserDependency, + SingleUserEncryptorDependency, + WhenDependency, +} from "../dependencies"; -import { StateConstraintsDependency } from "./state-constraints-dependency"; +import { SubjectConstraintsDependency } from "./state-constraints-dependency"; /** dependencies accepted by the user state subject */ export type UserStateSubjectDependencies = Simplify< - SingleUserDependency & + RequireExactlyOne< + SingleUserDependency & SingleUserEncryptorDependency, + "singleUserEncryptor$" | "singleUserId$" + > & Partial & Partial> & - Partial> & { + Partial> & { /** Compute the next stored value. If this is not set, values * provided to `next` unconditionally override state. * @param current the value stored in state diff --git a/libs/common/src/tools/state/user-state-subject.spec.ts b/libs/common/src/tools/state/user-state-subject.spec.ts index 73971da4ef9..9f5475df9de 100644 --- a/libs/common/src/tools/state/user-state-subject.spec.ts +++ b/libs/common/src/tools/state/user-state-subject.spec.ts @@ -1,14 +1,50 @@ import { BehaviorSubject, of, Subject } from "rxjs"; +import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; import { awaitAsync, FakeSingleUserState, ObservableTracker } from "../../../spec"; +import { UserBound } from "../dependencies"; +import { PrivateClassifier } from "../private-classifier"; import { StateConstraints } from "../types"; +import { ClassifiedFormat } from "./classified-format"; +import { ObjectKey } from "./object-key"; +import { UserEncryptor } from "./user-encryptor.abstraction"; import { UserStateSubject } from "./user-state-subject"; const SomeUser = "some user" as UserId; type TestType = { foo: string }; +const SomeKey = new UserKeyDefinition(GENERATOR_DISK, "TestKey", { + deserializer: (d) => d as TestType, + clearOn: [], +}); + +const SomeObjectKey = { + target: "object", + key: "TestObjectKey", + state: GENERATOR_DISK, + classifier: new PrivateClassifier(), + format: "classified", + options: { + deserializer: (d) => d as TestType, + clearOn: ["logout"], + }, +} satisfies ObjectKey; + +const SomeEncryptor: UserEncryptor = { + userId: SomeUser, + + encrypt(secret) { + const tmp: any = secret; + return Promise.resolve({ foo: `encrypt(${tmp.foo})` } as any); + }, + + decrypt(secret) { + const tmp: any = JSON.parse(secret.encryptedString); + return Promise.resolve({ foo: `decrypt(${tmp.foo})` } as any); + }, +}; function fooMaxLength(maxLength: number): StateConstraints { return Object.freeze({ @@ -43,7 +79,11 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const nextValue = jest.fn((_, next) => next); const when$ = new BehaviorSubject(true); - const subject = new UserStateSubject(state, { singleUserId$, nextValue, when$ }); + const subject = new UserStateSubject(SomeKey, () => state, { + singleUserId$, + nextValue, + when$, + }); // the interleaved await asyncs are only necessary b/c `nextValue` is called asynchronously subject.next({ foo: "next" }); @@ -65,7 +105,11 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const nextValue = jest.fn((_, next) => next); const when$ = new BehaviorSubject(true); - const subject = new UserStateSubject(state, { singleUserId$, nextValue, when$ }); + const subject = new UserStateSubject(SomeKey, () => state, { + singleUserId$, + nextValue, + when$, + }); // the interleaved await asyncs are only necessary b/c `nextValue` is called asynchronously subject.next({ foo: "next" }); @@ -79,11 +123,35 @@ describe("UserStateSubject", () => { expect(nextValue).toHaveBeenCalledTimes(1); }); + it("ignores repeated singleUserEncryptor$ emissions", async () => { + // this test looks for `nextValue` because a subscription isn't necessary for + // the subject to update + const initialValue: TestType = { foo: "init" }; + const state = new FakeSingleUserState(SomeUser, initialValue); + const nextValue = jest.fn((_, next) => next); + const singleUserEncryptor$ = new BehaviorSubject({ userId: SomeUser, encryptor: null }); + const subject = new UserStateSubject(SomeKey, () => state, { + nextValue, + singleUserEncryptor$, + }); + + // the interleaved await asyncs are only necessary b/c `nextValue` is called asynchronously + subject.next({ foo: "next" }); + await awaitAsync(); + singleUserEncryptor$.next({ userId: SomeUser, encryptor: null }); + await awaitAsync(); + singleUserEncryptor$.next({ userId: SomeUser, encryptor: null }); + singleUserEncryptor$.next({ userId: SomeUser, encryptor: null }); + await awaitAsync(); + + expect(nextValue).toHaveBeenCalledTimes(1); + }); + it("waits for constraints$", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new Subject>(); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject); constraints$.next(fooMaxLength(3)); @@ -91,13 +159,28 @@ describe("UserStateSubject", () => { expect(initResult).toEqual({ foo: "ini" }); }); + + it("waits for singleUserEncryptor$", async () => { + const state = new FakeSingleUserState>>( + SomeUser, + { id: null, secret: '{"foo":"init"}', disclosed: {} }, + ); + const singleUserEncryptor$ = new Subject>(); + const subject = new UserStateSubject(SomeObjectKey, () => state, { singleUserEncryptor$ }); + const tracker = new ObservableTracker(subject); + + singleUserEncryptor$.next({ userId: SomeUser, encryptor: SomeEncryptor }); + const [initResult] = await tracker.pauseUntilReceived(1); + + expect(initResult).toEqual({ foo: "decrypt(init)" }); + }); }); describe("next", () => { it("emits the next value", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); const expected: TestType = { foo: "next" }; let actual: TestType = null; @@ -114,7 +197,7 @@ describe("UserStateSubject", () => { const initialState = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialState); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let actual: TestType = null; subject.subscribe((value) => { @@ -132,7 +215,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); const shouldUpdate = jest.fn(() => true); - const subject = new UserStateSubject(state, { singleUserId$, shouldUpdate }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, shouldUpdate }); const nextVal: TestType = { foo: "next" }; subject.next(nextVal); @@ -147,7 +230,7 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const shouldUpdate = jest.fn(() => true); const dependencyValue = { bar: "dependency" }; - const subject = new UserStateSubject(state, { + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, shouldUpdate, dependencies$: of(dependencyValue), @@ -165,7 +248,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); const shouldUpdate = jest.fn(() => true); - const subject = new UserStateSubject(state, { singleUserId$, shouldUpdate }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, shouldUpdate }); const expected: TestType = { foo: "next" }; let actual: TestType = null; @@ -183,7 +266,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); const shouldUpdate = jest.fn(() => false); - const subject = new UserStateSubject(state, { singleUserId$, shouldUpdate }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, shouldUpdate }); subject.next({ foo: "next" }); await awaitAsync(); @@ -200,7 +283,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); const nextValue = jest.fn((_, next) => next); - const subject = new UserStateSubject(state, { singleUserId$, nextValue }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, nextValue }); const nextVal: TestType = { foo: "next" }; subject.next(nextVal); @@ -215,7 +298,7 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const nextValue = jest.fn((_, next) => next); const dependencyValue = { bar: "dependency" }; - const subject = new UserStateSubject(state, { + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, nextValue, dependencies$: of(dependencyValue), @@ -236,7 +319,11 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const nextValue = jest.fn((_, next) => next); const when$ = new BehaviorSubject(true); - const subject = new UserStateSubject(state, { singleUserId$, nextValue, when$ }); + const subject = new UserStateSubject(SomeKey, () => state, { + singleUserId$, + nextValue, + when$, + }); const nextVal: TestType = { foo: "next" }; subject.next(nextVal); @@ -253,7 +340,11 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const nextValue = jest.fn((_, next) => next); const when$ = new BehaviorSubject(false); - const subject = new UserStateSubject(state, { singleUserId$, nextValue, when$ }); + const subject = new UserStateSubject(SomeKey, () => state, { + singleUserId$, + nextValue, + when$, + }); const nextVal: TestType = { foo: "next" }; subject.next(nextVal); @@ -265,42 +356,52 @@ describe("UserStateSubject", () => { expect(nextValue).toHaveBeenCalled(); }); - it("waits to evaluate nextValue until singleUserId$ emits", async () => { - // this test looks for `nextValue` because a subscription isn't necessary for + it("waits to evaluate `UserState.update` until singleUserId$ emits", async () => { + // this test looks for `nextMock` because a subscription isn't necessary for // the subject to update. const initialValue: TestType = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new Subject(); - const nextValue = jest.fn((_, next) => next); - const subject = new UserStateSubject(state, { singleUserId$, nextValue }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); + // precondition: subject doesn't update after `next` const nextVal: TestType = { foo: "next" }; subject.next(nextVal); await awaitAsync(); - expect(nextValue).not.toHaveBeenCalled(); + expect(state.nextMock).not.toHaveBeenCalled(); + singleUserId$.next(SomeUser); await awaitAsync(); - expect(nextValue).toHaveBeenCalled(); + expect(state.nextMock).toHaveBeenCalledWith({ foo: "next" }); }); - it("applies constraints$ on init", async () => { - const state = new FakeSingleUserState(SomeUser, { foo: "init" }); - const singleUserId$ = new BehaviorSubject(SomeUser); - const constraints$ = new BehaviorSubject(fooMaxLength(2)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); - const tracker = new ObservableTracker(subject); + it("waits to evaluate `UserState.update` until singleUserEncryptor$ emits", async () => { + const state = new FakeSingleUserState>>( + SomeUser, + { id: null, secret: '{"foo":"init"}', disclosed: null }, + ); + const singleUserEncryptor$ = new Subject>(); + const subject = new UserStateSubject(SomeObjectKey, () => state, { singleUserEncryptor$ }); - const [result] = await tracker.pauseUntilReceived(1); + // precondition: subject doesn't update after `next` + const nextVal: TestType = { foo: "next" }; + subject.next(nextVal); + await awaitAsync(); + expect(state.nextMock).not.toHaveBeenCalled(); - expect(result).toEqual({ foo: "in" }); + singleUserEncryptor$.next({ userId: SomeUser, encryptor: SomeEncryptor }); + await awaitAsync(); + + const encrypted = { foo: "encrypt(next)" }; + expect(state.nextMock).toHaveBeenCalledWith({ id: null, secret: encrypted, disclosed: null }); }); it("applies dynamic constraints", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(DynamicFooMaxLength); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject); const expected: TestType = { foo: "next" }; const emission = tracker.expectEmission(); @@ -311,24 +412,11 @@ describe("UserStateSubject", () => { expect(actual).toEqual({ foo: "" }); }); - it("applies constraints$ on constraints$ emission", async () => { - const state = new FakeSingleUserState(SomeUser, { foo: "init" }); - const singleUserId$ = new BehaviorSubject(SomeUser); - const constraints$ = new BehaviorSubject(fooMaxLength(2)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); - const tracker = new ObservableTracker(subject); - - constraints$.next(fooMaxLength(1)); - const [, result] = await tracker.pauseUntilReceived(2); - - expect(result).toEqual({ foo: "i" }); - }); - it("applies constraints$ on next", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(fooMaxLength(2)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject); subject.next({ foo: "next" }); @@ -341,7 +429,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(fooMaxLength(2)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject); constraints$.next(fooMaxLength(3)); @@ -355,13 +443,17 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new Subject>(); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); - const tracker = new ObservableTracker(subject); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); + const results: any[] = []; + subject.subscribe((r) => { + results.push(r); + }); subject.next({ foo: "next" }); constraints$.next(fooMaxLength(3)); + await awaitAsync(); // `init` is also waiting and is processed before `next` - const [, nextResult] = await tracker.pauseUntilReceived(2); + const [, nextResult] = results; expect(nextResult).toEqual({ foo: "nex" }); }); @@ -370,7 +462,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(fooMaxLength(3)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject); constraints$.error({ some: "error" }); @@ -384,7 +476,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(fooMaxLength(3)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject); constraints$.complete(); @@ -399,7 +491,7 @@ describe("UserStateSubject", () => { it("emits errors", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); const expected: TestType = { foo: "error" }; let actual: TestType = null; @@ -418,7 +510,7 @@ describe("UserStateSubject", () => { const initialState = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialState); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let actual: TestType = null; subject.subscribe({ @@ -437,7 +529,7 @@ describe("UserStateSubject", () => { const initialState = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialState); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let shouldNotRun = false; subject.subscribe({ @@ -457,7 +549,7 @@ describe("UserStateSubject", () => { it("emits completes", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let actual = false; subject.subscribe({ @@ -475,7 +567,7 @@ describe("UserStateSubject", () => { const initialState = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialState); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let shouldNotRun = false; subject.subscribe({ @@ -496,7 +588,7 @@ describe("UserStateSubject", () => { const initialState = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialState); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let timesRun = 0; subject.subscribe({ @@ -513,11 +605,36 @@ describe("UserStateSubject", () => { }); describe("subscribe", () => { + it("applies constraints$ on init", async () => { + const state = new FakeSingleUserState(SomeUser, { foo: "init" }); + const singleUserId$ = new BehaviorSubject(SomeUser); + const constraints$ = new BehaviorSubject(fooMaxLength(2)); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); + const tracker = new ObservableTracker(subject); + + const [result] = await tracker.pauseUntilReceived(1); + + expect(result).toEqual({ foo: "in" }); + }); + + it("applies constraints$ on constraints$ emission", async () => { + const state = new FakeSingleUserState(SomeUser, { foo: "init" }); + const singleUserId$ = new BehaviorSubject(SomeUser); + const constraints$ = new BehaviorSubject(fooMaxLength(2)); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); + const tracker = new ObservableTracker(subject); + + constraints$.next(fooMaxLength(1)); + const [, result] = await tracker.pauseUntilReceived(2); + + expect(result).toEqual({ foo: "i" }); + }); + it("completes when singleUserId$ completes", async () => { const initialValue: TestType = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); let actual = false; subject.subscribe({ @@ -531,12 +648,32 @@ describe("UserStateSubject", () => { expect(actual).toBeTruthy(); }); + it("completes when singleUserId$ completes", async () => { + const state = new FakeSingleUserState>>( + SomeUser, + { id: null, secret: '{"foo":"init"}', disclosed: null }, + ); + const singleUserEncryptor$ = new Subject>(); + const subject = new UserStateSubject(SomeObjectKey, () => state, { singleUserEncryptor$ }); + + let actual = false; + subject.subscribe({ + complete: () => { + actual = true; + }, + }); + singleUserEncryptor$.complete(); + await awaitAsync(); + + expect(actual).toBeTruthy(); + }); + it("completes when when$ completes", async () => { const initialValue: TestType = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); const when$ = new BehaviorSubject(true); - const subject = new UserStateSubject(state, { singleUserId$, when$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, when$ }); let actual = false; subject.subscribe({ @@ -557,7 +694,7 @@ describe("UserStateSubject", () => { const initialValue: TestType = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); const errorUserId = "error" as UserId; let error = false; @@ -572,11 +709,32 @@ describe("UserStateSubject", () => { expect(error).toEqual({ expectedUserId: SomeUser, actualUserId: errorUserId }); }); + it("errors when singleUserEncryptor$ changes", async () => { + const state = new FakeSingleUserState>>( + SomeUser, + { id: null, secret: '{"foo":"init"}', disclosed: null }, + ); + const singleUserEncryptor$ = new Subject>(); + const subject = new UserStateSubject(SomeObjectKey, () => state, { singleUserEncryptor$ }); + const errorUserId = "error" as UserId; + + let error = false; + subject.subscribe({ + error: (e: unknown) => { + error = e as any; + }, + }); + singleUserEncryptor$.next({ userId: errorUserId, encryptor: SomeEncryptor }); + await awaitAsync(); + + expect(error).toEqual({ expectedUserId: SomeUser, actualUserId: errorUserId }); + }); + it("errors when singleUserId$ errors", async () => { const initialValue: TestType = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); const expected = { error: "description" }; let actual = false; @@ -591,12 +749,31 @@ describe("UserStateSubject", () => { expect(actual).toEqual(expected); }); + it("errors when singleUserEncryptor$ errors", async () => { + const initialValue: TestType = { foo: "init" }; + const state = new FakeSingleUserState(SomeUser, initialValue); + const singleUserEncryptor$ = new Subject>(); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserEncryptor$ }); + const expected = { error: "description" }; + + let actual = false; + subject.subscribe({ + error: (e: unknown) => { + actual = e as any; + }, + }); + singleUserEncryptor$.error(expected); + await awaitAsync(); + + expect(actual).toEqual(expected); + }); + it("errors when when$ errors", async () => { const initialValue: TestType = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialValue); const singleUserId$ = new BehaviorSubject(SomeUser); const when$ = new BehaviorSubject(true); - const subject = new UserStateSubject(state, { singleUserId$, when$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, when$ }); const expected = { error: "description" }; let actual = false; @@ -616,7 +793,7 @@ describe("UserStateSubject", () => { it("returns the userId to which the subject is bound", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new Subject(); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); expect(subject.userId).toEqual(SomeUser); }); @@ -626,7 +803,7 @@ describe("UserStateSubject", () => { it("emits the next value with an empty constraint", async () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); const tracker = new ObservableTracker(subject.withConstraints$); const expected: TestType = { foo: "next" }; const emission = tracker.expectEmission(); @@ -642,7 +819,7 @@ describe("UserStateSubject", () => { const initialState = { foo: "init" }; const state = new FakeSingleUserState(SomeUser, initialState); const singleUserId$ = new BehaviorSubject(SomeUser); - const subject = new UserStateSubject(state, { singleUserId$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$ }); const tracker = new ObservableTracker(subject.withConstraints$); subject.complete(); @@ -657,7 +834,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(fooMaxLength(2)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); const expected = fooMaxLength(1); const emission = tracker.expectEmission(); @@ -673,7 +850,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(DynamicFooMaxLength); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); const expected: TestType = { foo: "next" }; const emission = tracker.expectEmission(); @@ -690,7 +867,7 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const expected = fooMaxLength(2); const constraints$ = new BehaviorSubject(expected); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); const emission = tracker.expectEmission(); @@ -705,7 +882,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new BehaviorSubject(fooMaxLength(2)); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); const expected = fooMaxLength(3); constraints$.next(expected); @@ -722,7 +899,7 @@ describe("UserStateSubject", () => { const state = new FakeSingleUserState(SomeUser, { foo: "init" }); const singleUserId$ = new BehaviorSubject(SomeUser); const constraints$ = new Subject>(); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); const expected = fooMaxLength(3); @@ -740,7 +917,7 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const expected = fooMaxLength(3); const constraints$ = new BehaviorSubject(expected); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); constraints$.error({ some: "error" }); @@ -756,7 +933,7 @@ describe("UserStateSubject", () => { const singleUserId$ = new BehaviorSubject(SomeUser); const expected = fooMaxLength(3); const constraints$ = new BehaviorSubject(expected); - const subject = new UserStateSubject(state, { singleUserId$, constraints$ }); + const subject = new UserStateSubject(SomeKey, () => state, { singleUserId$, constraints$ }); const tracker = new ObservableTracker(subject.withConstraints$); constraints$.complete(); diff --git a/libs/common/src/tools/state/user-state-subject.ts b/libs/common/src/tools/state/user-state-subject.ts index 61a9e87c686..89f19ac3c73 100644 --- a/libs/common/src/tools/state/user-state-subject.ts +++ b/libs/common/src/tools/state/user-state-subject.ts @@ -5,15 +5,10 @@ import { ReplaySubject, filter, map, - Subject, takeUntil, pairwise, - combineLatest, distinctUntilChanged, BehaviorSubject, - race, - ignoreElements, - endWith, startWith, Observable, Subscription, @@ -22,16 +17,32 @@ import { combineLatestWith, catchError, EMPTY, + concatMap, + OperatorFunction, + pipe, + first, + withLatestFrom, + scan, + skip, } from "rxjs"; -import { SingleUserState } from "@bitwarden/common/platform/state"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SingleUserState, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { UserId } from "@bitwarden/common/types/guid"; -import { WithConstraints } from "../types"; +import { UserBound } from "../dependencies"; +import { anyComplete, ready, withLatestReady } from "../rx"; +import { Constraints, SubjectConstraints, WithConstraints } from "../types"; -import { IdentityConstraint } from "./identity-state-constraint"; +import { ClassifiedFormat, isClassifiedFormat } from "./classified-format"; +import { unconstrained$ } from "./identity-state-constraint"; +import { isObjectKey, ObjectKey, toUserKeyDefinition } from "./object-key"; import { isDynamic } from "./state-constraints-dependency"; +import { UserEncryptor } from "./user-encryptor.abstraction"; import { UserStateSubjectDependencies } from "./user-state-subject-dependencies"; +type Constrained = { constraints: Readonly>; state: State }; + /** * Adapt a state provider to an rxjs subject. * @@ -44,14 +55,20 @@ import { UserStateSubjectDependencies } from "./user-state-subject-dependencies" * @template State the state stored by the subject * @template Dependencies use-specific dependencies provided by the user. */ -export class UserStateSubject +export class UserStateSubject< + State extends object, + Secret = State, + Disclosed = never, + Dependencies = null, + > extends Observable implements SubjectLike { /** - * Instantiates the user state subject - * @param state the backing store of the subject - * @param dependencies tailor the subject's behavior for a particular + * Instantiates the user state subject bound to a persistent backing store + * @param key identifies the persistent backing store + * @param getState creates a persistent backing store using a key + * @param context tailor the subject's behavior for a particular * purpose. * @param dependencies.when$ blocks updates to the state subject until * this becomes true. When this occurs, only the last-received update @@ -61,91 +78,304 @@ export class UserStateSubject * is available. */ constructor( - private state: SingleUserState, - private dependencies: UserStateSubjectDependencies, + private key: UserKeyDefinition | ObjectKey, + getState: (key: UserKeyDefinition) => SingleUserState, + private context: UserStateSubjectDependencies, ) { super(); + if (isObjectKey(this.key)) { + // classification and encryption only supported with `ObjectKey` + this.objectKey = this.key; + this.stateKey = toUserKeyDefinition(this.key); + this.state = getState(this.stateKey); + } else { + // raw state access granted with `UserKeyDefinition` + this.objectKey = null; + this.stateKey = this.key as UserKeyDefinition; + this.state = getState(this.stateKey); + } + // normalize dependencies - const when$ = (this.dependencies.when$ ?? new BehaviorSubject(true)).pipe( - distinctUntilChanged(), + const when$ = (this.context.when$ ?? new BehaviorSubject(true)).pipe(distinctUntilChanged()); + + // manage dependencies through replay subjects since `UserStateSubject` + // reads them in multiple places + const encryptor$ = new ReplaySubject(1); + const { singleUserId$, singleUserEncryptor$ } = this.context; + this.encryptor(singleUserEncryptor$ ?? singleUserId$).subscribe(encryptor$); + + const constraints$ = new ReplaySubject>(1); + (this.context.constraints$ ?? unconstrained$()) + .pipe( + // FIXME: this should probably log that an error occurred + catchError(() => EMPTY), + ) + .subscribe(constraints$); + + const dependencies$ = new ReplaySubject(1); + if (this.context.dependencies$) { + this.context.dependencies$.subscribe(dependencies$); + } else { + dependencies$.next(null); + } + + // wire output before input so that output normalizes the current state + // before any `next` value is processed + this.outputSubscription = this.state.state$ + .pipe(this.declassify(encryptor$), this.adjust(combineLatestWith(constraints$))) + .subscribe(this.output); + + const last$ = new ReplaySubject(1); + this.output + .pipe( + last(), + map((o) => o.state), + ) + .subscribe(last$); + + // the update stream simulates the stateProvider's "shouldUpdate" + // functionality & applies policy + const updates$ = concat( + this.input.pipe( + this.when(when$), + this.adjust(withLatestReady(constraints$)), + this.prepareUpdate(this, dependencies$), + ), + // when the output subscription completes, its last-emitted value + // loops around to the input for finalization + last$.pipe(this.fix(constraints$), this.prepareUpdate(last$, dependencies$)), ); - const userIdAvailable$ = this.dependencies.singleUserId$.pipe( - startWith(state.userId), + + // classification/encryption bound to the input subscription's lifetime + // to ensure that `fix` has access to the encryptor key + // + // FIXME: this should probably timeout when a lock occurs + this.inputSubscription = updates$ + .pipe(this.classify(encryptor$), takeUntil(anyComplete([when$, this.input, encryptor$]))) + .subscribe({ + next: (state) => this.onNext(state), + error: (e: unknown) => this.onError(e), + complete: () => this.onComplete(), + }); + } + + private stateKey: UserKeyDefinition; + private objectKey: ObjectKey; + + private encryptor( + singleUserEncryptor$: Observable | UserId>, + ): Observable { + return singleUserEncryptor$.pipe( + // normalize inputs + map((maybe): UserBound<"encryptor", UserEncryptor> => { + if (typeof maybe === "object" && "encryptor" in maybe) { + return maybe; + } else if (typeof maybe === "string") { + return { encryptor: null, userId: maybe as UserId }; + } else { + throw new Error(`Invalid encryptor input received for ${this.key.key}.`); + } + }), + // fail the stream if the state desyncs from the bound userId + startWith({ userId: this.state.userId, encryptor: null } as UserBound< + "encryptor", + UserEncryptor + >), pairwise(), - map(([expectedUserId, actualUserId]) => { - if (expectedUserId === actualUserId) { - return true; + map(([expected, actual]) => { + if (expected.userId === actual.userId) { + return actual; } else { - throw { expectedUserId, actualUserId }; + throw { + expectedUserId: expected.userId, + actualUserId: actual.userId, + }; } }), + // reduce emissions to when encryptor changes distinctUntilChanged(), + map(({ encryptor }) => encryptor), ); - const constraints$ = ( - this.dependencies.constraints$ ?? new BehaviorSubject(new IdentityConstraint()) - ).pipe( - // FIXME: this should probably log that an error occurred - catchError(() => EMPTY), + } + + private when(when$: Observable): OperatorFunction { + return pipe( + combineLatestWith(when$.pipe(distinctUntilChanged())), + filter(([_, when]) => !!when), + map(([input]) => input), ); + } + + private prepareUpdate( + init$: Observable, + dependencies$: Observable, + ): OperatorFunction, State> { + return (input$) => + concat( + // `init$` becomes the accumulator for `scan` + init$.pipe( + first(), + map((init) => [init, null] as const), + ), + input$.pipe( + map((constrained) => constrained.state), + withLatestFrom(dependencies$), + ), + ).pipe( + // scan only emits values that can cause updates + scan(([prev], [pending, dependencies]) => { + const shouldUpdate = this.context.shouldUpdate?.(prev, pending, dependencies) ?? true; + if (shouldUpdate) { + // actual update + const next = this.context.nextValue?.(prev, pending, dependencies) ?? pending; + return [next, dependencies]; + } else { + // false update + return [prev, null]; + } + }), + // the first emission primes `scan`s aggregator + skip(1), + map(([state]) => state), + + // clean up false updates + distinctUntilChanged(), + ); + } - // normalize input in case this `UserStateSubject` is not the only - // observer of the backing store - const input$ = combineLatest([this.input, constraints$]).pipe( - map(([input, constraints]) => { - const calibration = isDynamic(constraints) ? constraints.calibrate(input) : constraints; - const state = calibration.adjust(input); - return state; + private adjust( + withConstraints: OperatorFunction]>, + ): OperatorFunction> { + return pipe( + // how constraints are blended with incoming emissions varies: + // * `output` needs to emit when constraints update + // * `input` needs to wait until a message flows through the pipe + withConstraints, + map(([loadedState, constraints]) => { + // bypass nulls + if (!loadedState) { + return { + constraints: {} as Constraints, + state: null, + } satisfies Constrained; + } + + const calibration = isDynamic(constraints) + ? constraints.calibrate(loadedState) + : constraints; + const adjusted = calibration.adjust(loadedState); + + return { + constraints: calibration.constraints, + state: adjusted, + }; }), ); + } - // when the output subscription completes, its last-emitted value - // loops around to the input for finalization - const finalize$ = this.pipe( - last(), + private fix( + constraints$: Observable>, + ): OperatorFunction> { + return pipe( combineLatestWith(constraints$), - map(([output, constraints]) => { - const calibration = isDynamic(constraints) ? constraints.calibrate(output) : constraints; - const state = calibration.fix(output); - return state; + map(([loadedState, constraints]) => { + const calibration = isDynamic(constraints) + ? constraints.calibrate(loadedState) + : constraints; + const fixed = calibration.fix(loadedState); + + return { + constraints: calibration.constraints, + state: fixed, + }; }), ); - const updates$ = concat(input$, finalize$); + } - // observe completion - const whenComplete$ = when$.pipe(ignoreElements(), endWith(true)); - const inputComplete$ = this.input.pipe(ignoreElements(), endWith(true)); - const userIdComplete$ = this.dependencies.singleUserId$.pipe(ignoreElements(), endWith(true)); - const completion$ = race(whenComplete$, inputComplete$, userIdComplete$); + private declassify(encryptor$: Observable): OperatorFunction { + // short-circuit if they key lacks encryption support + if (!this.objectKey || this.objectKey.format === "plain") { + return (input$) => input$ as Observable; + } - // wire output before input so that output normalizes the current state - // before any `next` value is processed - this.outputSubscription = this.state.state$ - .pipe( - combineLatestWith(constraints$), - map(([rawState, constraints]) => { - const calibration = isDynamic(constraints) - ? constraints.calibrate(rawState) - : constraints; - const state = calibration.adjust(rawState); - return { - constraints: calibration.constraints, - state, - }; + // if the key supports encryption, enable encryptor support + if (this.objectKey && this.objectKey.format === "classified") { + return pipe( + combineLatestWith(encryptor$), + concatMap(async ([input, encryptor]) => { + // pass through null values + if (input === null || input === undefined) { + return null; + } + + // fail fast if the format is incorrect + if (!isClassifiedFormat(input)) { + throw new Error(`Cannot declassify ${this.key.key}; unknown format.`); + } + + // decrypt classified data + const { secret, disclosed } = input; + const encrypted = EncString.fromJSON(secret); + const decryptedSecret = await encryptor.decrypt(encrypted); + + // assemble into proper state + const declassified = this.objectKey.classifier.declassify(disclosed, decryptedSecret); + const state = this.objectKey.options.deserializer(declassified); + + return state; }), - ) - .subscribe(this.output); - this.inputSubscription = combineLatest([updates$, when$, userIdAvailable$]) - .pipe( - filter(([_, when]) => when), - map(([state]) => state), - takeUntil(completion$), - ) - .subscribe({ - next: (r) => this.onNext(r), - error: (e: unknown) => this.onError(e), - complete: () => this.onComplete(), - }); + ); + } + + throw new Error(`unknown serialization format: ${this.objectKey.format}`); + } + + private classify(encryptor$: Observable): OperatorFunction { + // short-circuit if they key lacks encryption support; `encryptor` is + // readied to preserve `dependencies.singleUserId$` emission contract + if (!this.objectKey || this.objectKey.format === "plain") { + return pipe( + ready(encryptor$), + map((input) => input as unknown), + ); + } + + // if the key supports encryption, enable encryptor support + if (this.objectKey && this.objectKey.format === "classified") { + return pipe( + withLatestReady(encryptor$), + concatMap(async ([input, encryptor]) => { + // fail fast if there's no value + if (input === null || input === undefined) { + return null; + } + + // split data by classification level + const serialized = JSON.parse(JSON.stringify(input)); + const classified = this.objectKey.classifier.classify(serialized); + + // protect data + const encrypted = await encryptor.encrypt(classified.secret); + const secret = JSON.parse(JSON.stringify(encrypted)); + + // wrap result in classified format envelope for storage + const envelope = { + id: null as void, + secret, + disclosed: classified.disclosed, + } satisfies ClassifiedFormat; + + // deliberate type erasure; the type is restored during `declassify` + return envelope as unknown; + }), + ); + } + + // FIXME: add "encrypted" format --> key contains encryption logic + // CONSIDER: should "classified format" algorithm be embedded in subject keys...? + + throw new Error(`unknown serialization format: ${this.objectKey.format}`); } /** The userId to which the subject is bound. @@ -177,7 +407,8 @@ export class UserStateSubject // using subjects to ensure the right semantics are followed; // if greater efficiency becomes desirable, consider implementing // `SubjectLike` directly - private input = new Subject(); + private input = new ReplaySubject(1); + private state: SingleUserState; private readonly output = new ReplaySubject>(1); /** A stream containing settings and their last-applied constraints. */ @@ -188,25 +419,8 @@ export class UserStateSubject private inputSubscription: Unsubscribable; private outputSubscription: Unsubscribable; - private onNext(value: State) { - const nextValue = this.dependencies.nextValue ?? ((_: State, next: State) => next); - const shouldUpdate = this.dependencies.shouldUpdate ?? ((_: State) => true); - - this.state - .update( - (state, dependencies) => { - const next = nextValue(state, value, dependencies); - return next; - }, - { - shouldUpdate(current, dependencies) { - const update = shouldUpdate(current, value, dependencies); - return update; - }, - combineLatestWith: this.dependencies.dependencies$, - }, - ) - .catch((e: any) => this.onError(e)); + private onNext(value: unknown) { + this.state.update(() => value).catch((e: any) => this.onError(e)); } private onError(value: any) { @@ -232,8 +446,8 @@ export class UserStateSubject private dispose() { if (!this.isDisposed) { // clean up internal subscriptions - this.inputSubscription.unsubscribe(); - this.outputSubscription.unsubscribe(); + this.inputSubscription?.unsubscribe(); + this.outputSubscription?.unsubscribe(); this.inputSubscription = null; this.outputSubscription = null; diff --git a/libs/common/src/tools/types.ts b/libs/common/src/tools/types.ts index ec1903e6225..9b746924278 100644 --- a/libs/common/src/tools/types.ts +++ b/libs/common/src/tools/types.ts @@ -1,5 +1,7 @@ import { Simplify } from "type-fest"; +import { IntegrationId } from "./integration"; + /** Constraints that are shared by all primitive field types */ type PrimitiveConstraint = { /** `true` indicates the field is required; otherwise the field is optional */ @@ -129,6 +131,8 @@ export type StateConstraints = { fix: (state: State) => State; }; +export type SubjectConstraints = StateConstraints | DynamicStateConstraints; + /** Options that provide contextual information about the application state * when a generator is invoked. */ @@ -144,4 +148,7 @@ export type VaultItemRequest = { /** Options that provide contextual information about the application state * when a generator is invoked. */ -export type GenerationRequest = Partial; +export type GenerationRequest = Partial & + Partial<{ + integration: IntegrationId | null; + }>; diff --git a/libs/tools/generator/components/src/credential-generator.component.html b/libs/tools/generator/components/src/credential-generator.component.html index 53df58c8480..4c9fb9e7e49 100644 --- a/libs/tools/generator/components/src/credential-generator.component.html +++ b/libs/tools/generator/components/src/credential-generator.component.html @@ -3,7 +3,7 @@ fullWidth class="tw-mb-4" [selected]="(root$ | async).nav" - (selectedChange)="onRootChanged($event)" + (selectedChange)="onRootChanged({ nav: $event })" attr.aria-label="{{ 'type' | i18n }}" > @@ -35,23 +35,23 @@ -
{{ "options" | i18n }}
+

{{ "options" | i18n }}

- + {{ "type" | i18n }} @@ -60,18 +60,29 @@
{{ "options" | i18n }}
}}
+
+ + {{ "service" | i18n }} + + +
+ diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts index 86093beecd6..25aff97f16c 100644 --- a/libs/tools/generator/components/src/credential-generator.component.ts +++ b/libs/tools/generator/components/src/credential-generator.component.ts @@ -2,11 +2,12 @@ import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } fro import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, - concat, + catchError, + combineLatest, + combineLatestWith, distinctUntilChanged, filter, map, - of, ReplaySubject, Subject, switchMap, @@ -16,25 +17,32 @@ import { 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 { IntegrationId } from "@bitwarden/common/tools/integration"; import { UserId } from "@bitwarden/common/types/guid"; +import { ToastService } from "@bitwarden/components"; import { Option } from "@bitwarden/components/src/select/option"; import { + AlgorithmInfo, CredentialAlgorithm, CredentialCategory, - CredentialGeneratorInfo, CredentialGeneratorService, GeneratedCredential, Generators, + getForwarderConfiguration, isEmailAlgorithm, + isForwarderIntegration, isPasswordAlgorithm, + isSameAlgorithm, isUsernameAlgorithm, - PasswordAlgorithm, + toCredentialGeneratorConfiguration, } from "@bitwarden/generator-core"; -/** root category that drills into username and email categories */ +// constants used to identify navigation selections that are not +// generator algorithms const IDENTIFIER = "identifier"; -/** options available for the top-level navigation */ -type RootNavValue = PasswordAlgorithm | typeof IDENTIFIER; +const FORWARDER = "forwarder"; +const NONE_SELECTED = "none"; @Component({ selector: "tools-credential-generator", @@ -43,6 +51,8 @@ type RootNavValue = PasswordAlgorithm | typeof IDENTIFIER; export class CredentialGeneratorComponent implements OnInit, OnDestroy { constructor( private generatorService: CredentialGeneratorService, + private toastService: ToastService, + private logService: LogService, private i18nService: I18nService, private accountService: AccountService, private zone: NgZone, @@ -59,59 +69,25 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { @Output() readonly onGenerated = new EventEmitter(); - protected root$ = new BehaviorSubject<{ nav: RootNavValue }>({ + protected root$ = new BehaviorSubject<{ nav: string }>({ nav: null, }); - /** - * Emits the copy button aria-label respective of the selected credential type - * - * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. - */ - protected credentialTypeCopyLabel$ = this.root$.pipe( - map(({ nav }) => { - if (nav === "password") { - return this.i18nService.t("copyPassword"); - } - - if (nav === "passphrase") { - return this.i18nService.t("copyPassphrase"); - } - - return this.i18nService.t("copyUsername"); - }), - ); - - /** - * Emits the generate button aria-label respective of the selected credential type - * - * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. - */ - protected credentialTypeGenerateLabel$ = this.root$.pipe( - map(({ nav }) => { - if (nav === "password") { - return this.i18nService.t("generatePassword"); - } - - if (nav === "passphrase") { - return this.i18nService.t("generatePassphrase"); - } - - return this.i18nService.t("generateUsername"); - }), - ); - - protected onRootChanged(nav: RootNavValue) { + protected onRootChanged(value: { nav: string }) { // prevent subscription cycle - if (this.root$.value.nav !== nav) { + if (this.root$.value.nav !== value.nav) { this.zone.run(() => { - this.root$.next({ nav }); + this.root$.next(value); }); } } protected username = this.formBuilder.group({ - nav: [null as CredentialAlgorithm], + nav: [null as string], + }); + + protected forwarder = this.formBuilder.group({ + nav: [null as string], }); async ngOnInit() { @@ -130,16 +106,29 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { this.generatorService .algorithms$(["email", "username"], { userId$: this.userId$ }) .pipe( - map((algorithms) => this.toOptions(algorithms)), + map((algorithms) => { + const usernames = algorithms.filter((a) => !isForwarderIntegration(a.id)); + const usernameOptions = this.toOptions(usernames); + usernameOptions.push({ value: FORWARDER, label: this.i18nService.t("forwardedEmail") }); + + const forwarders = algorithms.filter((a) => isForwarderIntegration(a.id)); + const forwarderOptions = this.toOptions(forwarders); + forwarderOptions.unshift({ value: NONE_SELECTED, label: this.i18nService.t("select") }); + + return [usernameOptions, forwarderOptions] as const; + }), takeUntil(this.destroyed), ) - .subscribe(this.usernameOptions$); + .subscribe(([usernames, forwarders]) => { + this.usernameOptions$.next(usernames); + this.forwarderOptions$.next(forwarders); + }); this.generatorService .algorithms$("password", { userId$: this.userId$ }) .pipe( map((algorithms) => { - const options = this.toOptions(algorithms) as Option[]; + const options = this.toOptions(algorithms); options.push({ value: IDENTIFIER, label: this.i18nService.t("username") }); return options; }), @@ -149,7 +138,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { this.algorithm$ .pipe( - map((a) => a?.descriptionKey && this.i18nService.t(a?.descriptionKey)), + map((a) => a?.description), takeUntil(this.destroyed), ) .subscribe((hint) => { @@ -162,7 +151,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { this.algorithm$ .pipe( - map((a) => a.category), + map((a) => a?.category), distinctUntilChanged(), takeUntil(this.destroyed), ) @@ -177,7 +166,22 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { // wire up the generator this.algorithm$ .pipe( + filter((algorithm) => !!algorithm), switchMap((algorithm) => this.typeToGenerator$(algorithm.id)), + catchError((error: unknown, generator) => { + if (typeof error === "string") { + this.toastService.showToast({ + message: error, + variant: "error", + title: "", + }); + } else { + this.logService.error(error); + } + + // continue with origin stream + return generator; + }), takeUntil(this.destroyed), ) .subscribe((generated) => { @@ -189,35 +193,116 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { }); }); - // assume the last-visible generator algorithm is the user's preferred one - const preferences = await this.generatorService.preferences({ singleUserId$: this.userId$ }); + // normalize cascade selections; introduce subjects to allow changes + // from user selections and changes from preference updates to + // update the template + type CascadeValue = { nav: string; algorithm?: CredentialAlgorithm }; + const activeRoot$ = new Subject(); + const activeIdentifier$ = new Subject(); + const activeForwarder$ = new Subject(); + this.root$ .pipe( - filter(({ nav }) => !!nav), - switchMap((root) => { - if (root.nav === IDENTIFIER) { - return concat(of(this.username.value), this.username.valueChanges); + map( + (root): CascadeValue => + root.nav === IDENTIFIER + ? { nav: root.nav } + : { nav: root.nav, algorithm: JSON.parse(root.nav) }, + ), + takeUntil(this.destroyed), + ) + .subscribe(activeRoot$); + + this.username.valueChanges + .pipe( + map( + (username): CascadeValue => + username.nav === FORWARDER + ? { nav: username.nav } + : { nav: username.nav, algorithm: JSON.parse(username.nav) }, + ), + takeUntil(this.destroyed), + ) + .subscribe(activeIdentifier$); + + this.forwarder.valueChanges + .pipe( + map( + (forwarder): CascadeValue => + forwarder.nav === NONE_SELECTED + ? { nav: forwarder.nav } + : { nav: forwarder.nav, algorithm: JSON.parse(forwarder.nav) }, + ), + takeUntil(this.destroyed), + ) + .subscribe(activeForwarder$); + + // update forwarder cascade visibility + combineLatest([activeRoot$, activeIdentifier$, activeForwarder$]) + .pipe( + map(([root, username, forwarder]) => { + const showForwarder = !root.algorithm && !username.algorithm; + const forwarderId = + showForwarder && isForwarderIntegration(forwarder.algorithm) + ? forwarder.algorithm.forwarder + : null; + return [showForwarder, forwarderId] as const; + }), + distinctUntilChanged((prev, next) => prev[0] === next[0] && prev[1] === next[1]), + takeUntil(this.destroyed), + ) + .subscribe(([showForwarder, forwarderId]) => { + // update subjects within the angular zone so that the + // template bindings refresh immediately + this.zone.run(() => { + this.showForwarder$.next(showForwarder); + this.forwarderId$.next(forwarderId); + }); + }); + + // update active algorithm + combineLatest([activeRoot$, activeIdentifier$, activeForwarder$]) + .pipe( + map(([root, username, forwarder]) => { + const selection = root.algorithm ?? username.algorithm ?? forwarder.algorithm; + if (selection) { + return this.generatorService.algorithm(selection); } else { - return of(root as { nav: PasswordAlgorithm }); + return null; } }), - filter(({ nav }) => !!nav), + distinctUntilChanged((prev, next) => isSameAlgorithm(prev?.id, next?.id)), + takeUntil(this.destroyed), + ) + .subscribe((algorithm) => { + // update subjects within the angular zone so that the + // template bindings refresh immediately + this.zone.run(() => { + this.algorithm$.next(algorithm); + }); + }); + + // assume the last-selected generator algorithm is the user's preferred one + const preferences = await this.generatorService.preferences({ singleUserId$: this.userId$ }); + this.algorithm$ + .pipe( + filter((algorithm) => !!algorithm), withLatestFrom(preferences), takeUntil(this.destroyed), ) - .subscribe(([{ nav: algorithm }, preference]) => { + .subscribe(([algorithm, preference]) => { function setPreference(category: CredentialCategory) { const p = preference[category]; - p.algorithm = algorithm; + p.algorithm = algorithm.id; p.updated = new Date(); } // `is*Algorithm` decides `algorithm`'s type, which flows into `setPreference` - if (isEmailAlgorithm(algorithm)) { + if (isEmailAlgorithm(algorithm.id)) { setPreference("email"); - } else if (isUsernameAlgorithm(algorithm)) { + } else if (isUsernameAlgorithm(algorithm.id)) { setPreference("username"); - } else if (isPasswordAlgorithm(algorithm)) { + } else if (isPasswordAlgorithm(algorithm.id)) { setPreference("password"); } else { return; @@ -227,34 +312,74 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { }); // populate the form with the user's preferences to kick off interactivity - preferences.pipe(takeUntil(this.destroyed)).subscribe(({ email, username, password }) => { - // the last preference set by the user "wins" - const userNav = email.updated > username.updated ? email : username; - const rootNav: any = userNav.updated > password.updated ? IDENTIFIER : password.algorithm; - const credentialType = rootNav === IDENTIFIER ? userNav.algorithm : password.algorithm; - - // update navigation; break subscription loop - this.onRootChanged(rootNav); - this.username.setValue({ nav: userNav.algorithm }, { emitEvent: false }); - - // load algorithm metadata - const algorithm = this.generatorService.algorithm(credentialType); + preferences + .pipe( + map(({ email, username, password }) => { + const forwarderPref = isForwarderIntegration(email.algorithm) ? email : null; + const usernamePref = email.updated > username.updated ? email : username; + + // inject drilldown flags + const forwarderNav = !forwarderPref + ? NONE_SELECTED + : JSON.stringify(forwarderPref.algorithm); + const userNav = forwarderPref ? FORWARDER : JSON.stringify(usernamePref.algorithm); + const rootNav = + usernamePref.updated > password.updated + ? IDENTIFIER + : JSON.stringify(password.algorithm); + + // construct cascade metadata + const cascade = { + root: { + selection: { nav: rootNav }, + active: { + nav: rootNav, + algorithm: rootNav === IDENTIFIER ? null : password.algorithm, + } as CascadeValue, + }, + username: { + selection: { nav: userNav }, + active: { + nav: userNav, + algorithm: forwarderPref ? null : usernamePref.algorithm, + }, + }, + forwarder: { + selection: { nav: forwarderNav }, + active: { + nav: forwarderNav, + algorithm: forwarderPref?.algorithm, + }, + }, + }; + + return cascade; + }), + takeUntil(this.destroyed), + ) + .subscribe(({ root, username, forwarder }) => { + // update navigation; break subscription loop + this.onRootChanged(root.selection); + this.username.setValue(username.selection, { emitEvent: false }); + this.forwarder.setValue(forwarder.selection, { emitEvent: false }); + + // update cascade visibility + activeRoot$.next(root.active); + activeIdentifier$.next(username.active); + activeForwarder$.next(forwarder.active); + }); - // update subjects within the angular zone so that the - // template bindings refresh immediately + // automatically regenerate when the algorithm switches if the algorithm + // allows it; otherwise set a placeholder + this.algorithm$.pipe(takeUntil(this.destroyed)).subscribe((a) => { this.zone.run(() => { - this.algorithm$.next(algorithm); + if (!a || a.onlyOnRequest) { + this.value$.next("-"); + } else { + this.generate$.next(); + } }); }); - - // generate on load unless the generator prohibits it - this.algorithm$ - .pipe( - distinctUntilChanged((prev, next) => prev.id === next.id), - filter((a) => !a.onlyOnRequest), - takeUntil(this.destroyed), - ) - .subscribe(() => this.generate$.next()); } private typeToGenerator$(type: CredentialAlgorithm) { @@ -278,20 +403,61 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { case "passphrase": return this.generatorService.generate$(Generators.passphrase, dependencies); + } - default: - throw new Error(`Invalid generator type: "${type}"`); + if (isForwarderIntegration(type)) { + const forwarder = getForwarderConfiguration(type.forwarder); + const configuration = toCredentialGeneratorConfiguration(forwarder); + const generator = this.generatorService.generate$(configuration, dependencies); + return generator; } + + throw new Error(`Invalid generator type: "${type}"`); } + /** Lists the top-level credential types supported by the component. + * @remarks This is string-typed because angular doesn't support + * structural equality for objects, which prevents `CredentialAlgorithm` + * from being selectable within a dropdown when its value contains a + * `ForwarderIntegration`. + */ + protected rootOptions$ = new BehaviorSubject[]>([]); + + /** Lists the credential types of the username algorithm box. */ + protected usernameOptions$ = new BehaviorSubject[]>([]); + /** Lists the credential types of the username algorithm box. */ - protected usernameOptions$ = new BehaviorSubject[]>([]); + protected forwarderOptions$ = new BehaviorSubject[]>([]); - /** Lists the top-level credential types supported by the component. */ - protected rootOptions$ = new BehaviorSubject[]>([]); + /** Tracks the currently selected forwarder. */ + protected forwarderId$ = new BehaviorSubject(null); + + /** Tracks forwarder control visibility */ + protected showForwarder$ = new BehaviorSubject(false); /** tracks the currently selected credential type */ - protected algorithm$ = new ReplaySubject(1); + protected algorithm$ = new ReplaySubject(1); + + protected showAlgorithm$ = this.algorithm$.pipe( + combineLatestWith(this.showForwarder$), + map(([algorithm, showForwarder]) => (showForwarder ? null : algorithm)), + ); + + /** + * Emits the copy button aria-label respective of the selected credential type + */ + protected credentialTypeCopyLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ copy }) => copy), + ); + + /** + * Emits the generate button aria-label respective of the selected credential type + */ + protected credentialTypeGenerateLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ copy }) => copy), + ); /** Emits hint key for the currently selected credential type */ protected credentialTypeHint$ = new ReplaySubject(1); @@ -308,10 +474,10 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { /** Emits when a new credential is requested */ protected readonly generate$ = new Subject(); - private toOptions(algorithms: CredentialGeneratorInfo[]) { - const options: Option[] = algorithms.map((algorithm) => ({ - value: algorithm.id, - label: this.i18nService.t(algorithm.nameKey), + private toOptions(algorithms: AlgorithmInfo[]) { + const options: Option[] = algorithms.map((algorithm) => ({ + value: JSON.stringify(algorithm.id), + label: algorithm.name, })); return options; diff --git a/libs/tools/generator/components/src/forwarder-settings.component.html b/libs/tools/generator/components/src/forwarder-settings.component.html new file mode 100644 index 00000000000..64566fa9562 --- /dev/null +++ b/libs/tools/generator/components/src/forwarder-settings.component.html @@ -0,0 +1,16 @@ +
+ + {{ "forwarderDomainName" | i18n }} + + {{ "forwarderDomainNameHint" | i18n }} + + + {{ "apiKey" | i18n }} + + + + + {{ "selfHostBaseUrl" | i18n }} + + +
diff --git a/libs/tools/generator/components/src/forwarder-settings.component.ts b/libs/tools/generator/components/src/forwarder-settings.component.ts new file mode 100644 index 00000000000..a1e6c7acfd8 --- /dev/null +++ b/libs/tools/generator/components/src/forwarder-settings.component.ts @@ -0,0 +1,195 @@ +import { + Component, + EventEmitter, + Input, + OnChanges, + OnDestroy, + OnInit, + Output, + SimpleChanges, +} from "@angular/core"; +import { FormBuilder } from "@angular/forms"; +import { + BehaviorSubject, + concatMap, + map, + ReplaySubject, + skip, + Subject, + switchAll, + switchMap, + takeUntil, + withLatestFrom, +} from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { IntegrationId } from "@bitwarden/common/tools/integration"; +import { UserId } from "@bitwarden/common/types/guid"; +import { + CredentialGeneratorConfiguration, + CredentialGeneratorService, + getForwarderConfiguration, + NoPolicy, + toCredentialGeneratorConfiguration, +} from "@bitwarden/generator-core"; + +import { completeOnAccountSwitch, toValidators } from "./util"; + +const Controls = Object.freeze({ + domain: "domain", + token: "token", + baseUrl: "baseUrl", +}); + +/** Options group for forwarder integrations */ +@Component({ + selector: "tools-forwarder-settings", + templateUrl: "forwarder-settings.component.html", +}) +export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy { + /** Instantiates the component + * @param accountService queries user availability + * @param generatorService settings and policy logic + * @param formBuilder reactive form controls + */ + constructor( + private formBuilder: FormBuilder, + private generatorService: CredentialGeneratorService, + private accountService: AccountService, + ) {} + + /** Binds the component to a specific user's settings. + * When this input is not provided, the form binds to the active + * user + */ + @Input() + userId: UserId | null; + + @Input({ required: true }) + forwarder: IntegrationId; + + /** Emits settings updates and completes if the settings become unavailable. + * @remarks this does not emit the initial settings. If you would like + * to receive live settings updates including the initial update, + * use `CredentialGeneratorService.settings$(...)` instead. + */ + @Output() + readonly onUpdated = new EventEmitter(); + + /** The template's control bindings */ + protected settings = this.formBuilder.group({ + [Controls.domain]: [""], + [Controls.token]: [""], + [Controls.baseUrl]: [""], + }); + + private forwarderId$ = new ReplaySubject(1); + + async ngOnInit() { + const singleUserId$ = this.singleUserId$(); + + const forwarder$ = new ReplaySubject>(1); + this.forwarderId$ + .pipe( + map((id) => getForwarderConfiguration(id)), + // type erasure necessary because the configuration properties are + // determined dynamically at runtime + // FIXME: this can be eliminated by unifying the forwarder settings types; + // see `ForwarderConfiguration<...>` for details. + map((forwarder) => toCredentialGeneratorConfiguration(forwarder)), + takeUntil(this.destroyed$), + ) + .subscribe((forwarder) => { + this.displayDomain = forwarder.request.includes("domain"); + this.displayToken = forwarder.request.includes("token"); + this.displayBaseUrl = forwarder.request.includes("baseUrl"); + + forwarder$.next(forwarder); + }); + + const settings$$ = forwarder$.pipe( + concatMap((forwarder) => this.generatorService.settings(forwarder, { singleUserId$ })), + ); + + // bind settings to the reactive form + settings$$.pipe(switchAll(), takeUntil(this.destroyed$)).subscribe((settings) => { + // skips reactive event emissions to break a subscription cycle + this.settings.patchValue(settings as any, { emitEvent: false }); + }); + + // bind policy to the reactive form + forwarder$ + .pipe( + switchMap((forwarder) => { + const constraints$ = this.generatorService + .policy$(forwarder, { userId$: singleUserId$ }) + .pipe(map(({ constraints }) => [constraints, forwarder] as const)); + + return constraints$; + }), + takeUntil(this.destroyed$), + ) + .subscribe(([constraints, forwarder]) => { + for (const name in Controls) { + const control = this.settings.get(name); + if (forwarder.request.includes(name as any)) { + control.enable({ emitEvent: false }); + control.setValidators( + // the configuration's type erasure affects `toValidators` as well + toValidators(name, forwarder, constraints), + ); + } else { + control.disable({ emitEvent: false }); + control.clearValidators(); + } + } + }); + + // the first emission is the current value; subsequent emissions are updates + settings$$ + .pipe( + map((settings$) => settings$.pipe(skip(1))), + switchAll(), + takeUntil(this.destroyed$), + ) + .subscribe(this.onUpdated); + + // now that outputs are set up, connect inputs + this.settings.valueChanges + .pipe(withLatestFrom(settings$$), takeUntil(this.destroyed$)) + .subscribe(([value, settings]) => { + settings.next(value); + }); + } + + ngOnChanges(changes: SimpleChanges): void { + this.refresh$.complete(); + if ("forwarder" in changes) { + this.forwarderId$.next(this.forwarder); + } + } + + protected displayDomain: boolean; + protected displayToken: boolean; + protected displayBaseUrl: boolean; + + private singleUserId$() { + // FIXME: this branch should probably scan for the user and make sure + // the account is unlocked + if (this.userId) { + return new BehaviorSubject(this.userId as UserId).asObservable(); + } + + return this.accountService.activeAccount$.pipe( + completeOnAccountSwitch(), + takeUntil(this.destroyed$), + ); + } + + private readonly refresh$ = new Subject(); + + private readonly destroyed$ = new Subject(); + ngOnDestroy(): void { + this.destroyed$.complete(); + } +} diff --git a/libs/tools/generator/components/src/generator.module.ts b/libs/tools/generator/components/src/generator.module.ts index 96622774a3f..58117bec495 100644 --- a/libs/tools/generator/components/src/generator.module.ts +++ b/libs/tools/generator/components/src/generator.module.ts @@ -5,8 +5,11 @@ import { ReactiveFormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { SafeInjectionToken } from "@bitwarden/angular/services/injection-tokens"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { CardComponent, @@ -30,6 +33,7 @@ import { import { CatchallSettingsComponent } from "./catchall-settings.component"; import { CredentialGeneratorComponent } from "./credential-generator.component"; +import { ForwarderSettingsComponent } from "./forwarder-settings.component"; import { PassphraseSettingsComponent } from "./passphrase-settings.component"; import { PasswordGeneratorComponent } from "./password-generator.component"; import { PasswordSettingsComponent } from "./password-settings.component"; @@ -67,18 +71,27 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer"); safeProvider({ provide: CredentialGeneratorService, useClass: CredentialGeneratorService, - deps: [RANDOMIZER, StateProvider, PolicyService], + deps: [ + RANDOMIZER, + StateProvider, + PolicyService, + ApiService, + I18nService, + EncryptService, + CryptoService, + ], }), ], declarations: [ CatchallSettingsComponent, CredentialGeneratorComponent, + ForwarderSettingsComponent, SubaddressSettingsComponent, - UsernameSettingsComponent, PasswordGeneratorComponent, - PasswordSettingsComponent, PassphraseSettingsComponent, + PasswordSettingsComponent, UsernameGeneratorComponent, + UsernameSettingsComponent, ], exports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent], }) diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts index 37c40ce8b1b..f6ec1b17e2d 100644 --- a/libs/tools/generator/components/src/password-generator.component.ts +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -21,9 +21,9 @@ import { Generators, PasswordAlgorithm, GeneratedCredential, - CredentialGeneratorInfo, CredentialAlgorithm, isPasswordAlgorithm, + AlgorithmInfo, } from "@bitwarden/generator-core"; /** Options group for passwords */ @@ -52,36 +52,6 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy { /** tracks the currently selected credential type */ protected credentialType$ = new BehaviorSubject(null); - /** - * Emits the copy button aria-label respective of the selected credential - * - * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. - */ - protected credentialTypeCopyLabel$ = this.credentialType$.pipe( - map((cred) => { - if (cred === "password") { - return this.i18nService.t("copyPassword"); - } - - return this.i18nService.t("copyPassphrase"); - }), - ); - - /** - * Emits the generate button aria-label respective of the selected credential - * - * FIXME: Move label and logic to `AlgorithmInfo` within the `CredentialGeneratorService`. - */ - protected credentialTypeGenerateLabel$ = this.credentialType$.pipe( - map((cred) => { - if (cred === "password") { - return this.i18nService.t("generatePassword"); - } - - return this.i18nService.t("generatePassphrase"); - }), - ); - /** Emits the last generated value. */ protected readonly value$ = new BehaviorSubject(""); @@ -208,12 +178,28 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy { protected passwordOptions$ = new BehaviorSubject[]>([]); /** tracks the currently selected credential type */ - protected algorithm$ = new ReplaySubject(1); + protected algorithm$ = new ReplaySubject(1); + + /** + * Emits the copy button aria-label respective of the selected credential type + */ + protected credentialTypeCopyLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ copy }) => copy), + ); + + /** + * Emits the generate button aria-label respective of the selected credential type + */ + protected credentialTypeGenerateLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ copy }) => copy), + ); - private toOptions(algorithms: CredentialGeneratorInfo[]) { + private toOptions(algorithms: AlgorithmInfo[]) { const options: Option[] = algorithms.map((algorithm) => ({ value: algorithm.id, - label: this.i18nService.t(algorithm.nameKey), + label: this.i18nService.t(algorithm.name), })); return options; diff --git a/libs/tools/generator/components/src/username-generator.component.html b/libs/tools/generator/components/src/username-generator.component.html index ad8cd796123..3d175f32f78 100644 --- a/libs/tools/generator/components/src/username-generator.component.html +++ b/libs/tools/generator/components/src/username-generator.component.html @@ -3,45 +3,54 @@
- -
-
{{ "options" | i18n }}
+

{{ "options" | i18n }}

-
+ {{ "type" | i18n }} - + {{ credentialTypeHint$ | async }}
+
+ + {{ "service" | i18n }} + + +
+ this.toOptions(algorithms)), + map((algorithms) => { + const usernames = algorithms.filter((a) => !isForwarderIntegration(a.id)); + const usernameOptions = this.toOptions(usernames); + usernameOptions.push({ value: FORWARDER, label: this.i18nService.t("forwarder") }); + + const forwarders = algorithms.filter((a) => isForwarderIntegration(a.id)); + const forwarderOptions = this.toOptions(forwarders); + forwarderOptions.unshift({ value: NONE_SELECTED, label: this.i18nService.t("select") }); + + return [usernameOptions, forwarderOptions] as const; + }), takeUntil(this.destroyed), ) - .subscribe(this.typeOptions$); + .subscribe(([usernames, forwarders]) => { + this.typeOptions$.next(usernames); + this.forwarderOptions$.next(forwarders); + }); this.algorithm$ .pipe( - map((a) => a?.descriptionKey && this.i18nService.t(a?.descriptionKey)), + map((a) => a?.description), takeUntil(this.destroyed), ) .subscribe((hint) => { @@ -103,7 +137,22 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { // wire up the generator this.algorithm$ .pipe( + filter((algorithm) => !!algorithm), switchMap((algorithm) => this.typeToGenerator$(algorithm.id)), + catchError((error: unknown, generator) => { + if (typeof error === "string") { + this.toastService.showToast({ + message: error, + variant: "error", + title: "", + }); + } else { + this.logService.error(error); + } + + // continue with origin stream + return generator; + }), takeUntil(this.destroyed), ) .subscribe((generated) => { @@ -115,20 +164,96 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { }); }); + // normalize cascade selections; introduce subjects to allow changes + // from user selections and changes from preference updates to + // update the template + type CascadeValue = { nav: string; algorithm?: CredentialAlgorithm }; + const activeIdentifier$ = new Subject(); + const activeForwarder$ = new Subject(); + + this.username.valueChanges + .pipe( + map( + (username): CascadeValue => + username.nav === FORWARDER + ? { nav: username.nav } + : { nav: username.nav, algorithm: JSON.parse(username.nav) }, + ), + takeUntil(this.destroyed), + ) + .subscribe(activeIdentifier$); + + this.forwarder.valueChanges + .pipe( + map( + (forwarder): CascadeValue => + forwarder.nav === NONE_SELECTED + ? { nav: forwarder.nav } + : { nav: forwarder.nav, algorithm: JSON.parse(forwarder.nav) }, + ), + takeUntil(this.destroyed), + ) + .subscribe(activeForwarder$); + + // update forwarder cascade visibility + combineLatest([activeIdentifier$, activeForwarder$]) + .pipe( + map(([username, forwarder]) => { + const showForwarder = !username.algorithm; + const forwarderId = + showForwarder && isForwarderIntegration(forwarder.algorithm) + ? forwarder.algorithm.forwarder + : null; + return [showForwarder, forwarderId] as const; + }), + distinctUntilChanged((prev, next) => prev[0] === next[0] && prev[1] === next[1]), + takeUntil(this.destroyed), + ) + .subscribe(([showForwarder, forwarderId]) => { + // update subjects within the angular zone so that the + // template bindings refresh immediately + this.zone.run(() => { + this.showForwarder$.next(showForwarder); + this.forwarderId$.next(forwarderId); + }); + }); + + // update active algorithm + combineLatest([activeIdentifier$, activeForwarder$]) + .pipe( + map(([username, forwarder]) => { + const selection = username.algorithm ?? forwarder.algorithm; + if (selection) { + return this.generatorService.algorithm(selection); + } else { + return null; + } + }), + distinctUntilChanged((prev, next) => isSameAlgorithm(prev?.id, next?.id)), + takeUntil(this.destroyed), + ) + .subscribe((algorithm) => { + // update subjects within the angular zone so that the + // template bindings refresh immediately + this.zone.run(() => { + this.algorithm$.next(algorithm); + }); + }); + // assume the last-visible generator algorithm is the user's preferred one const preferences = await this.generatorService.preferences({ singleUserId$: this.userId$ }); - this.credential.valueChanges + this.algorithm$ .pipe( - filter(({ type }) => !!type), + filter((algorithm) => !!algorithm), withLatestFrom(preferences), takeUntil(this.destroyed), ) - .subscribe(([{ type }, preference]) => { - if (isEmailAlgorithm(type)) { - preference.email.algorithm = type; + .subscribe(([algorithm, preference]) => { + if (isEmailAlgorithm(algorithm.id)) { + preference.email.algorithm = algorithm.id; preference.email.updated = new Date(); - } else if (isUsernameAlgorithm(type)) { - preference.username.algorithm = type; + } else if (isUsernameAlgorithm(algorithm.id)) { + preference.username.algorithm = algorithm.id; preference.username.updated = new Date(); } else { return; @@ -137,31 +262,61 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { preferences.next(preference); }); - // populate the form with the user's preferences to kick off interactivity - preferences.pipe(takeUntil(this.destroyed)).subscribe(({ email, username }) => { - // this generator supports email & username; the last preference - // set by the user "wins" - const preference = email.updated > username.updated ? email.algorithm : username.algorithm; - - // break subscription loop - this.credential.setValue({ type: preference }, { emitEvent: false }); + preferences + .pipe( + map(({ email, username }) => { + const forwarderPref = isForwarderIntegration(email.algorithm) ? email : null; + const usernamePref = email.updated > username.updated ? email : username; + + // inject drilldown flags + const forwarderNav = !forwarderPref + ? NONE_SELECTED + : JSON.stringify(forwarderPref.algorithm); + const userNav = forwarderPref ? FORWARDER : JSON.stringify(usernamePref.algorithm); + + // construct cascade metadata + const cascade = { + username: { + selection: { nav: userNav }, + active: { + nav: userNav, + algorithm: forwarderPref ? null : usernamePref.algorithm, + }, + }, + forwarder: { + selection: { nav: forwarderNav }, + active: { + nav: forwarderNav, + algorithm: forwarderPref?.algorithm, + }, + }, + }; + + return cascade; + }), + takeUntil(this.destroyed), + ) + .subscribe(({ username, forwarder }) => { + // update navigation; break subscription loop + this.username.setValue(username.selection, { emitEvent: false }); + this.forwarder.setValue(forwarder.selection, { emitEvent: false }); + + // update cascade visibility + activeIdentifier$.next(username.active); + activeForwarder$.next(forwarder.active); + }); - const algorithm = this.generatorService.algorithm(preference); - // update subjects within the angular zone so that the - // template bindings refresh immediately + // automatically regenerate when the algorithm switches if the algorithm + // allows it; otherwise set a placeholder + this.algorithm$.pipe(takeUntil(this.destroyed)).subscribe((a) => { this.zone.run(() => { - this.algorithm$.next(algorithm); + if (!a || a.onlyOnRequest) { + this.value$.next("-"); + } else { + this.generate$.next(); + } }); }); - - // generate on load unless the generator prohibits it - this.algorithm$ - .pipe( - distinctUntilChanged((prev, next) => prev.id === next.id), - filter((a) => !a.onlyOnRequest), - takeUntil(this.destroyed), - ) - .subscribe(() => this.generate$.next()); } private typeToGenerator$(type: CredentialAlgorithm) { @@ -179,17 +334,52 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { case "username": return this.generatorService.generate$(Generators.username, dependencies); + } - default: - throw new Error(`Invalid generator type: "${type}"`); + if (isForwarderIntegration(type)) { + const forwarder = getForwarderConfiguration(type.forwarder); + const configuration = toCredentialGeneratorConfiguration(forwarder); + return this.generatorService.generate$(configuration, dependencies); } + + throw new Error(`Invalid generator type: "${type}"`); } /** Lists the credential types supported by the component. */ - protected typeOptions$ = new BehaviorSubject[]>([]); + protected typeOptions$ = new BehaviorSubject[]>([]); + + /** Tracks the currently selected forwarder. */ + protected forwarderId$ = new BehaviorSubject(null); + + /** Lists the credential types supported by the component. */ + protected forwarderOptions$ = new BehaviorSubject[]>([]); + + /** Tracks forwarder control visibility */ + protected showForwarder$ = new BehaviorSubject(false); /** tracks the currently selected credential type */ - protected algorithm$ = new ReplaySubject(1); + protected algorithm$ = new ReplaySubject(1); + + protected showAlgorithm$ = this.algorithm$.pipe( + combineLatestWith(this.showForwarder$), + map(([algorithm, showForwarder]) => (showForwarder ? null : algorithm)), + ); + + /** + * Emits the copy button aria-label respective of the selected credential type + */ + protected credentialTypeCopyLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ copy }) => copy), + ); + + /** + * Emits the generate button aria-label respective of the selected credential type + */ + protected credentialTypeGenerateLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ copy }) => copy), + ); /** Emits hint key for the currently selected credential type */ protected credentialTypeHint$ = new ReplaySubject(1); @@ -203,10 +393,10 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { /** Emits when a new credential is requested */ protected readonly generate$ = new Subject(); - private toOptions(algorithms: CredentialGeneratorInfo[]) { - const options: Option[] = algorithms.map((algorithm) => ({ - value: algorithm.id, - label: this.i18nService.t(algorithm.nameKey), + private toOptions(algorithms: AlgorithmInfo[]) { + const options: Option[] = algorithms.map((algorithm) => ({ + value: JSON.stringify(algorithm.id), + label: this.i18nService.t(algorithm.name), })); return options; diff --git a/libs/tools/generator/components/src/util.ts b/libs/tools/generator/components/src/util.ts index 2049a285e25..d6cd4e6fbaf 100644 --- a/libs/tools/generator/components/src/util.ts +++ b/libs/tools/generator/components/src/util.ts @@ -63,7 +63,7 @@ function getConstraint( ) { if (policy && key in policy) { return policy[key] ?? config[key]; - } else if (key in config) { + } else if (config && key in config) { return config[key]; } } diff --git a/libs/tools/generator/core/src/data/generator-types.ts b/libs/tools/generator/core/src/data/generator-types.ts index 6c351b82e33..e54ec34e497 100644 --- a/libs/tools/generator/core/src/data/generator-types.ts +++ b/libs/tools/generator/core/src/data/generator-types.ts @@ -5,7 +5,7 @@ export const PasswordAlgorithms = Object.freeze(["password", "passphrase"] as co export const UsernameAlgorithms = Object.freeze(["username"] as const); /** Types of email addresses that may be generated by the credential generator */ -export const EmailAlgorithms = Object.freeze(["catchall", "forwarder", "subaddress"] as const); +export const EmailAlgorithms = Object.freeze(["catchall", "subaddress"] as const); /** All types of credentials that may be generated by the credential generator */ export const CredentialAlgorithms = Object.freeze([ diff --git a/libs/tools/generator/core/src/data/generators.ts b/libs/tools/generator/core/src/data/generators.ts index 2c96b0c2d39..d86eb52a8fa 100644 --- a/libs/tools/generator/core/src/data/generators.ts +++ b/libs/tools/generator/core/src/data/generators.ts @@ -1,9 +1,15 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; +import { ApiSettings } from "@bitwarden/common/tools/integration/rpc"; import { IdentityConstraint } from "@bitwarden/common/tools/state/identity-state-constraint"; -import { Randomizer } from "../abstractions"; -import { EmailRandomizer, PasswordRandomizer, UsernameRandomizer } from "../engine"; +import { + EmailRandomizer, + ForwarderConfiguration, + PasswordRandomizer, + UsernameRandomizer, +} from "../engine"; +import { Forwarder } from "../engine/forwarder"; import { DefaultPolicyEvaluator, DynamicPasswordPolicyConstraints, @@ -25,6 +31,7 @@ import { CredentialGenerator, CredentialGeneratorConfiguration, EffUsernameGenerationOptions, + GeneratorDependencyProvider, NoPolicy, PassphraseGenerationOptions, PassphraseGeneratorPolicy, @@ -45,10 +52,15 @@ const PASSPHRASE = Object.freeze({ id: "passphrase", category: "password", nameKey: "passphrase", + generateKey: "generatePassphrase", + copyKey: "copyPassphrase", onlyOnRequest: false, + request: [], engine: { - create(randomizer: Randomizer): CredentialGenerator { - return new PasswordRandomizer(randomizer); + create( + dependencies: GeneratorDependencyProvider, + ): CredentialGenerator { + return new PasswordRandomizer(dependencies.randomizer); }, }, settings: { @@ -82,10 +94,15 @@ const PASSWORD = Object.freeze({ id: "password", category: "password", nameKey: "password", + generateKey: "generatePassword", + copyKey: "copyPassword", onlyOnRequest: false, + request: [], engine: { - create(randomizer: Randomizer): CredentialGenerator { - return new PasswordRandomizer(randomizer); + create( + dependencies: GeneratorDependencyProvider, + ): CredentialGenerator { + return new PasswordRandomizer(dependencies.randomizer); }, }, settings: { @@ -127,10 +144,15 @@ const USERNAME = Object.freeze({ id: "username", category: "username", nameKey: "randomWord", + generateKey: "generateUsername", + copyKey: "copyUsername", onlyOnRequest: false, + request: [], engine: { - create(randomizer: Randomizer): CredentialGenerator { - return new UsernameRandomizer(randomizer); + create( + dependencies: GeneratorDependencyProvider, + ): CredentialGenerator { + return new UsernameRandomizer(dependencies.randomizer); }, }, settings: { @@ -158,10 +180,15 @@ const CATCHALL = Object.freeze({ category: "email", nameKey: "catchallEmail", descriptionKey: "catchallEmailDesc", + generateKey: "generateEmail", + copyKey: "copyEmail", onlyOnRequest: false, + request: [], engine: { - create(randomizer: Randomizer): CredentialGenerator { - return new EmailRandomizer(randomizer); + create( + dependencies: GeneratorDependencyProvider, + ): CredentialGenerator { + return new EmailRandomizer(dependencies.randomizer); }, }, settings: { @@ -189,10 +216,15 @@ const SUBADDRESS = Object.freeze({ category: "email", nameKey: "plusAddressedEmail", descriptionKey: "plusAddressedEmailDesc", + generateKey: "generateEmail", + copyKey: "copyEmail", onlyOnRequest: false, + request: [], engine: { - create(randomizer: Randomizer): CredentialGenerator { - return new EmailRandomizer(randomizer); + create( + dependencies: GeneratorDependencyProvider, + ): CredentialGenerator { + return new EmailRandomizer(dependencies.randomizer); }, }, settings: { @@ -215,6 +247,48 @@ const SUBADDRESS = Object.freeze({ }, } satisfies CredentialGeneratorConfiguration); +export function toCredentialGeneratorConfiguration( + configuration: ForwarderConfiguration, +) { + const forwarder = Object.freeze({ + id: { forwarder: configuration.id }, + category: "email", + nameKey: configuration.name, + descriptionKey: "forwardedEmailDesc", + generateKey: "generateEmail", + copyKey: "copyEmail", + onlyOnRequest: true, + request: configuration.forwarder.request, + engine: { + create(dependencies: GeneratorDependencyProvider) { + // FIXME: figure out why `configuration` fails to typecheck + const config: any = configuration; + return new Forwarder(config, dependencies.client, dependencies.i18nService); + }, + }, + settings: { + initial: configuration.forwarder.defaultSettings, + constraints: configuration.forwarder.settingsConstraints, + account: configuration.forwarder.settings, + }, + policy: { + type: PolicyType.PasswordGenerator, + disabledValue: {}, + combine(_acc: NoPolicy, _policy: Policy) { + return {}; + }, + createEvaluator(_policy: NoPolicy) { + return new DefaultPolicyEvaluator(); + }, + toConstraints(_policy: NoPolicy) { + return new IdentityConstraint(); + }, + }, + } satisfies CredentialGeneratorConfiguration); + + return forwarder; +} + /** Generator configurations */ export const Generators = Object.freeze({ /** Passphrase generator configuration */ diff --git a/libs/tools/generator/core/src/data/integrations.ts b/libs/tools/generator/core/src/data/integrations.ts index 6132891b368..71c80fc9dbe 100644 --- a/libs/tools/generator/core/src/data/integrations.ts +++ b/libs/tools/generator/core/src/data/integrations.ts @@ -1,3 +1,7 @@ +import { IntegrationId } from "@bitwarden/common/tools/integration"; +import { ApiSettings } from "@bitwarden/common/tools/integration/rpc"; + +import { ForwarderConfiguration } from "../engine"; import { AddyIo } from "../integration/addy-io"; import { DuckDuckGo } from "../integration/duck-duck-go"; import { Fastmail } from "../integration/fastmail"; @@ -5,6 +9,13 @@ import { FirefoxRelay } from "../integration/firefox-relay"; import { ForwardEmail } from "../integration/forward-email"; import { SimpleLogin } from "../integration/simple-login"; +/** Fixed list of integrations available to the application + * @example + * + * // Use `toCredentialGeneratorConfiguration(id :ForwarderIntegration)` + * // to convert an integration to a generator configuration + * const generator = toCredentialGeneratorConfiguration(Integrations.AddyIo); + */ export const Integrations = Object.freeze({ AddyIo, DuckDuckGo, @@ -13,3 +24,15 @@ export const Integrations = Object.freeze({ ForwardEmail, SimpleLogin, } as const); + +const integrations = new Map(Object.values(Integrations).map((i) => [i.id, i])); + +export function getForwarderConfiguration(id: IntegrationId): ForwarderConfiguration { + const maybeForwarder = integrations.get(id); + + if (maybeForwarder && "forwarder" in maybeForwarder) { + return maybeForwarder as ForwarderConfiguration; + } else { + return null; + } +} diff --git a/libs/tools/generator/core/src/engine/forwarder-configuration.ts b/libs/tools/generator/core/src/engine/forwarder-configuration.ts index 95c9add140a..7813f457399 100644 --- a/libs/tools/generator/core/src/engine/forwarder-configuration.ts +++ b/libs/tools/generator/core/src/engine/forwarder-configuration.ts @@ -1,11 +1,14 @@ import { UserKeyDefinition } from "@bitwarden/common/platform/state"; import { IntegrationConfiguration } from "@bitwarden/common/tools/integration/integration-configuration"; -import { ApiSettings } from "@bitwarden/common/tools/integration/rpc"; +import { ApiSettings, SelfHostedApiSettings } from "@bitwarden/common/tools/integration/rpc"; import { IntegrationRequest } from "@bitwarden/common/tools/integration/rpc/integration-request"; import { RpcConfiguration } from "@bitwarden/common/tools/integration/rpc/rpc-definition"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; +import { Constraints } from "@bitwarden/common/tools/types"; import { ForwarderContext } from "./forwarder-context"; +import { EmailDomainSettings, EmailPrefixSettings } from "./settings"; /** Mixin for transmitting `getAccountId` result. */ export type AccountRequest = { @@ -24,8 +27,16 @@ export type GetAccountIdRpcDef< Request extends IntegrationRequest = IntegrationRequest, > = RpcConfiguration, string>; +export type ForwarderRequestFields = keyof (ApiSettings & + SelfHostedApiSettings & + EmailDomainSettings & + EmailPrefixSettings); + /** Forwarder-specific static definition */ export type ForwarderConfiguration< + // FIXME: simply forwarder settings to an object that has all + // settings properties. The runtime dynamism should be limited + // to which have values, not which have properties listed. Settings extends ApiSettings, Request extends IntegrationRequest = IntegrationRequest, > = IntegrationConfiguration & { @@ -34,12 +45,30 @@ export type ForwarderConfiguration< /** default value of all fields */ defaultSettings: Partial; - /** forwarder settings storage */ + settingsConstraints: Constraints; + + /** Well-known fields to display on the forwarder screen */ + request: readonly ForwarderRequestFields[]; + + /** forwarder settings storage + * @deprecated use local.settings instead + */ settings: UserKeyDefinition; - /** forwarder settings import buffer; `undefined` when there is no buffer. */ + /** forwarder settings import buffer; `undefined` when there is no buffer. + * @deprecated use local.settings import + */ importBuffer?: BufferedKeyDefinition; + /** locally stored data; forwarder-partitioned */ + local: { + /** integration settings storage */ + settings: ObjectKey; + + /** plaintext import buffer - used during data migrations */ + import?: ObjectKey, Settings>; + }; + /** createForwardingEmail RPC definition */ createForwardingEmail: CreateForwardingEmailRpcDef; diff --git a/libs/tools/generator/core/src/engine/forwarder.ts b/libs/tools/generator/core/src/engine/forwarder.ts new file mode 100644 index 00000000000..523c6fdf1ec --- /dev/null +++ b/libs/tools/generator/core/src/engine/forwarder.ts @@ -0,0 +1,75 @@ +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { + ApiSettings, + IntegrationRequest, + RestClient, +} from "@bitwarden/common/tools/integration/rpc"; +import { GenerationRequest } from "@bitwarden/common/tools/types"; + +import { CredentialGenerator, GeneratedCredential } from "../types"; + +import { AccountRequest, ForwarderConfiguration } from "./forwarder-configuration"; +import { ForwarderContext } from "./forwarder-context"; +import { CreateForwardingAddressRpc, GetAccountIdRpc } from "./rpc"; + +/** Generation algorithms that query an email forwarding service to + * create anonymized email addresses. + */ +export class Forwarder implements CredentialGenerator { + /** Instantiates the email forwarder engine + * @param configuration The forwarder to query + * @param client requests data from the forwarding service + * @param i18nService localizes messages sent to the forwarding service + * and user-addressable errors + */ + constructor( + private configuration: ForwarderConfiguration, + private client: RestClient, + private i18nService: I18nService, + ) {} + + async generate(request: GenerationRequest, settings: ApiSettings) { + const requestOptions: IntegrationRequest & AccountRequest = { website: request.website }; + + const getAccount = await this.getAccountId(this.configuration, settings); + if (getAccount) { + requestOptions.accountId = await this.client.fetchJson(getAccount, requestOptions); + } + + const create = this.createForwardingAddress(this.configuration, settings); + const result = await this.client.fetchJson(create, requestOptions); + const id = { forwarder: this.configuration.id }; + + return new GeneratedCredential(result, id, Date.now()); + } + + private createContext( + configuration: ForwarderConfiguration, + settings: Settings, + ) { + return new ForwarderContext(configuration, settings, this.i18nService); + } + + private createForwardingAddress( + configuration: ForwarderConfiguration, + settings: Settings, + ) { + const context = this.createContext(configuration, settings); + const rpc = new CreateForwardingAddressRpc(configuration, context); + return rpc; + } + + private getAccountId( + configuration: ForwarderConfiguration, + settings: Settings, + ) { + if (!configuration.forwarder.getAccountId) { + return null; + } + + const context = this.createContext(configuration, settings); + const rpc = new GetAccountIdRpc(configuration, context); + + return rpc; + } +} diff --git a/libs/tools/generator/core/src/integration/addy-io.ts b/libs/tools/generator/core/src/integration/addy-io.ts index 8f594827e95..2d265ca9bfc 100644 --- a/libs/tools/generator/core/src/integration/addy-io.ts +++ b/libs/tools/generator/core/src/integration/addy-io.ts @@ -1,11 +1,18 @@ -import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { + GENERATOR_DISK, + GENERATOR_MEMORY, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; import { IntegrationContext, IntegrationId } from "@bitwarden/common/tools/integration"; import { ApiSettings, IntegrationRequest, SelfHostedApiSettings, } from "@bitwarden/common/tools/integration/rpc"; +import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; +import { PublicClassifier } from "@bitwarden/common/tools/public-classifier"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { ForwarderConfiguration, ForwarderContext, EmailDomainSettings } from "../engine"; import { CreateForwardingEmailRpcDef } from "../engine/forwarder-configuration"; @@ -44,6 +51,40 @@ const createForwardingEmail = Object.freeze({ // forwarder configuration const forwarder = Object.freeze({ defaultSettings, + createForwardingEmail, + request: ["token", "baseUrl", "domain"], + settingsConstraints: { + token: { required: true }, + domain: { required: true }, + baseUrl: {}, + }, + local: { + settings: { + // FIXME: integration should issue keys at runtime + // based on integrationId & extension metadata + // e.g. key: "forwarder.AddyIo.local.settings", + key: "addyIoForwarder", + target: "object", + format: "classified", + classifier: new PrivateClassifier(), + state: GENERATOR_DISK, + options: { + deserializer: (value) => value, + clearOn: ["logout"], + }, + } satisfies ObjectKey, + import: { + key: "forwarder.AddyIo.local.import", + target: "object", + format: "plain", + classifier: new PublicClassifier(["token", "baseUrl", "domain"]), + state: GENERATOR_MEMORY, + options: { + deserializer: (value) => value, + clearOn: ["logout", "lock"], + }, + } satisfies ObjectKey, AddyIoSettings>, + }, settings: new UserKeyDefinition(GENERATOR_DISK, "addyIoForwarder", { deserializer: (value) => value, clearOn: [], @@ -52,7 +93,6 @@ const forwarder = Object.freeze({ deserializer: (value) => value, clearOn: ["logout"], }), - createForwardingEmail, } as const); export const AddyIo = Object.freeze({ diff --git a/libs/tools/generator/core/src/integration/duck-duck-go.ts b/libs/tools/generator/core/src/integration/duck-duck-go.ts index 0c13ac6b632..4c1d672cc60 100644 --- a/libs/tools/generator/core/src/integration/duck-duck-go.ts +++ b/libs/tools/generator/core/src/integration/duck-duck-go.ts @@ -1,7 +1,14 @@ -import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { + GENERATOR_DISK, + GENERATOR_MEMORY, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; import { IntegrationContext, IntegrationId } from "@bitwarden/common/tools/integration"; import { ApiSettings, IntegrationRequest } from "@bitwarden/common/tools/integration/rpc"; +import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; +import { PublicClassifier } from "@bitwarden/common/tools/public-classifier"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { ForwarderConfiguration, ForwarderContext } from "../engine"; import { CreateForwardingEmailRpcDef } from "../engine/forwarder-configuration"; @@ -36,6 +43,38 @@ const createForwardingEmail = Object.freeze({ // forwarder configuration const forwarder = Object.freeze({ defaultSettings, + createForwardingEmail, + request: ["token"], + settingsConstraints: { + token: { required: true }, + }, + local: { + settings: { + // FIXME: integration should issue keys at runtime + // based on integrationId & extension metadata + // e.g. key: "forwarder.DuckDuckGo.local.settings", + key: "duckDuckGoForwarder", + target: "object", + format: "classified", + classifier: new PrivateClassifier(), + state: GENERATOR_DISK, + options: { + deserializer: (value) => value, + clearOn: ["logout"], + }, + } satisfies ObjectKey, + import: { + key: "forwarder.DuckDuckGo.local.import", + target: "object", + format: "plain", + classifier: new PublicClassifier(["token"]), + state: GENERATOR_MEMORY, + options: { + deserializer: (value) => value, + clearOn: ["logout", "lock"], + }, + } satisfies ObjectKey, DuckDuckGoSettings>, + }, settings: new UserKeyDefinition(GENERATOR_DISK, "duckDuckGoForwarder", { deserializer: (value) => value, clearOn: [], @@ -44,7 +83,6 @@ const forwarder = Object.freeze({ deserializer: (value) => value, clearOn: ["logout"], }), - createForwardingEmail, } as const); // integration-wide configuration diff --git a/libs/tools/generator/core/src/integration/fastmail.ts b/libs/tools/generator/core/src/integration/fastmail.ts index 0987540e036..13aa8db6247 100644 --- a/libs/tools/generator/core/src/integration/fastmail.ts +++ b/libs/tools/generator/core/src/integration/fastmail.ts @@ -1,7 +1,14 @@ -import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { + GENERATOR_DISK, + GENERATOR_MEMORY, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; import { IntegrationContext, IntegrationId } from "@bitwarden/common/tools/integration"; import { ApiSettings, IntegrationRequest } from "@bitwarden/common/tools/integration/rpc"; +import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; +import { PublicClassifier } from "@bitwarden/common/tools/public-classifier"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { ForwarderConfiguration, @@ -101,6 +108,41 @@ const createForwardingEmail = Object.freeze({ // forwarder configuration const forwarder = Object.freeze({ defaultSettings, + createForwardingEmail, + getAccountId, + request: ["token"], + settingsConstraints: { + token: { required: true }, + domain: { required: true }, + prefix: {}, + }, + local: { + settings: { + // FIXME: integration should issue keys at runtime + // based on integrationId & extension metadata + // e.g. key: "forwarder.Fastmail.local.settings" + key: "fastmailForwarder", + target: "object", + format: "classified", + classifier: new PrivateClassifier(), + state: GENERATOR_DISK, + options: { + deserializer: (value) => value, + clearOn: ["logout"], + }, + } satisfies ObjectKey, + import: { + key: "forwarder.Fastmail.local.import", + target: "object", + format: "plain", + classifier: new PublicClassifier(["token"]), + state: GENERATOR_MEMORY, + options: { + deserializer: (value) => value, + clearOn: ["logout", "lock"], + }, + } satisfies ObjectKey, FastmailSettings>, + }, settings: new UserKeyDefinition(GENERATOR_DISK, "fastmailForwarder", { deserializer: (value) => value, clearOn: [], @@ -109,8 +151,6 @@ const forwarder = Object.freeze({ deserializer: (value) => value, clearOn: ["logout"], }), - createForwardingEmail, - getAccountId, } as const); // integration-wide configuration diff --git a/libs/tools/generator/core/src/integration/firefox-relay.ts b/libs/tools/generator/core/src/integration/firefox-relay.ts index 4feb8a0bd99..9c965a4c9cd 100644 --- a/libs/tools/generator/core/src/integration/firefox-relay.ts +++ b/libs/tools/generator/core/src/integration/firefox-relay.ts @@ -1,7 +1,14 @@ -import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { + GENERATOR_DISK, + GENERATOR_MEMORY, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; import { IntegrationContext, IntegrationId } from "@bitwarden/common/tools/integration"; import { ApiSettings, IntegrationRequest } from "@bitwarden/common/tools/integration/rpc"; +import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; +import { PublicClassifier } from "@bitwarden/common/tools/public-classifier"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { ForwarderConfiguration, ForwarderContext } from "../engine"; import { CreateForwardingEmailRpcDef } from "../engine/forwarder-configuration"; @@ -40,6 +47,38 @@ const createForwardingEmail = Object.freeze({ // forwarder configuration const forwarder = Object.freeze({ defaultSettings, + createForwardingEmail, + request: ["token"], + settingsConstraints: { + token: { required: true }, + }, + local: { + settings: { + // FIXME: integration should issue keys at runtime + // based on integrationId & extension metadata + // e.g. key: "forwarder.Firefox.local.settings", + key: "firefoxRelayForwarder", + target: "object", + format: "classified", + classifier: new PrivateClassifier(), + state: GENERATOR_DISK, + options: { + deserializer: (value) => value, + clearOn: ["logout"], + }, + } satisfies ObjectKey, + import: { + key: "forwarder.Firefox.local.import", + target: "object", + format: "plain", + classifier: new PublicClassifier(["token"]), + state: GENERATOR_MEMORY, + options: { + deserializer: (value) => value, + clearOn: ["logout", "lock"], + }, + } satisfies ObjectKey, FirefoxRelaySettings>, + }, settings: new UserKeyDefinition(GENERATOR_DISK, "firefoxRelayForwarder", { deserializer: (value) => value, clearOn: [], @@ -52,7 +91,6 @@ const forwarder = Object.freeze({ clearOn: ["logout"], }, ), - createForwardingEmail, } as const); // integration-wide configuration diff --git a/libs/tools/generator/core/src/integration/forward-email.ts b/libs/tools/generator/core/src/integration/forward-email.ts index c4ef21d9d30..a128159fcd6 100644 --- a/libs/tools/generator/core/src/integration/forward-email.ts +++ b/libs/tools/generator/core/src/integration/forward-email.ts @@ -1,7 +1,14 @@ -import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { + GENERATOR_DISK, + GENERATOR_MEMORY, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; import { IntegrationContext, IntegrationId } from "@bitwarden/common/tools/integration"; import { ApiSettings, IntegrationRequest } from "@bitwarden/common/tools/integration/rpc"; +import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; +import { PublicClassifier } from "@bitwarden/common/tools/public-classifier"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { ForwarderConfiguration, ForwarderContext, EmailDomainSettings } from "../engine"; import { CreateForwardingEmailRpcDef } from "../engine/forwarder-configuration"; @@ -43,6 +50,38 @@ const createForwardingEmail = Object.freeze({ // forwarder configuration const forwarder = Object.freeze({ defaultSettings, + request: ["token", "domain"], + settingsConstraints: { + token: { required: true }, + domain: { required: true }, + }, + local: { + settings: { + // FIXME: integration should issue keys at runtime + // based on integrationId & extension metadata + // e.g. key: "forwarder.ForwardEmail.local.settings", + key: "forwardEmailForwarder", + target: "object", + format: "classified", + classifier: new PrivateClassifier(), + state: GENERATOR_DISK, + options: { + deserializer: (value) => value, + clearOn: ["logout"], + }, + } satisfies ObjectKey, + import: { + key: "forwarder.ForwardEmail.local.import", + target: "object", + format: "plain", + classifier: new PublicClassifier(["token", "domain"]), + state: GENERATOR_MEMORY, + options: { + deserializer: (value) => value, + clearOn: ["logout", "lock"], + }, + } satisfies ObjectKey, ForwardEmailSettings>, + }, settings: new UserKeyDefinition(GENERATOR_DISK, "forwardEmailForwarder", { deserializer: (value) => value, clearOn: [], diff --git a/libs/tools/generator/core/src/integration/simple-login.ts b/libs/tools/generator/core/src/integration/simple-login.ts index 88730d0578e..d4b297fc37e 100644 --- a/libs/tools/generator/core/src/integration/simple-login.ts +++ b/libs/tools/generator/core/src/integration/simple-login.ts @@ -1,11 +1,18 @@ -import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { + GENERATOR_DISK, + GENERATOR_MEMORY, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; import { IntegrationContext, IntegrationId } from "@bitwarden/common/tools/integration"; import { ApiSettings, IntegrationRequest, SelfHostedApiSettings, } from "@bitwarden/common/tools/integration/rpc"; +import { PrivateClassifier } from "@bitwarden/common/tools/private-classifier"; +import { PublicClassifier } from "@bitwarden/common/tools/public-classifier"; import { BufferedKeyDefinition } from "@bitwarden/common/tools/state/buffered-key-definition"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { ForwarderConfiguration, ForwarderContext } from "../engine"; import { CreateForwardingEmailRpcDef } from "../engine/forwarder-configuration"; @@ -45,6 +52,38 @@ const createForwardingEmail = Object.freeze({ // forwarder configuration const forwarder = Object.freeze({ defaultSettings, + createForwardingEmail, + request: ["token", "baseUrl"], + settingsConstraints: { + token: { required: true }, + }, + local: { + settings: { + // FIXME: integration should issue keys at runtime + // based on integrationId & extension metadata + // e.g. key: "forwarder.SimpleLogin.local.settings", + key: "simpleLoginForwarder", + target: "object", + format: "classified", + classifier: new PrivateClassifier(), + state: GENERATOR_DISK, + options: { + deserializer: (value) => value, + clearOn: ["logout"], + }, + } satisfies ObjectKey, + import: { + key: "forwarder.SimpleLogin.local.import", + target: "object", + format: "plain", + classifier: new PublicClassifier(["token", "baseUrl"]), + state: GENERATOR_MEMORY, + options: { + deserializer: (value) => value, + clearOn: ["logout", "lock"], + }, + } satisfies ObjectKey, SimpleLoginSettings>, + }, settings: new UserKeyDefinition(GENERATOR_DISK, "simpleLoginForwarder", { deserializer: (value) => value, clearOn: [], @@ -57,7 +96,6 @@ const forwarder = Object.freeze({ clearOn: ["logout"], }, ), - createForwardingEmail, } as const); // integration-wide configuration diff --git a/libs/tools/generator/core/src/rx.spec.ts b/libs/tools/generator/core/src/rx.spec.ts deleted file mode 100644 index b98e79bb074..00000000000 --- a/libs/tools/generator/core/src/rx.spec.ts +++ /dev/null @@ -1,352 +0,0 @@ -import { EmptyError, Subject, tap } from "rxjs"; - -import { anyComplete, on, ready } from "./rx"; - -describe("anyComplete", () => { - it("emits true when its input completes", () => { - const input$ = new Subject(); - - const emissions: boolean[] = []; - anyComplete(input$).subscribe((e) => emissions.push(e)); - input$.complete(); - - expect(emissions).toEqual([true]); - }); - - it("completes when its input is already complete", () => { - const input = new Subject(); - input.complete(); - - let completed = false; - anyComplete(input).subscribe({ complete: () => (completed = true) }); - - expect(completed).toBe(true); - }); - - it("completes when any input completes", () => { - const input$ = new Subject(); - const completing$ = new Subject(); - - let completed = false; - anyComplete([input$, completing$]).subscribe({ complete: () => (completed = true) }); - completing$.complete(); - - expect(completed).toBe(true); - }); - - it("ignores emissions", () => { - const input$ = new Subject(); - - const emissions: boolean[] = []; - anyComplete(input$).subscribe((e) => emissions.push(e)); - input$.next(1); - input$.next(2); - input$.complete(); - - expect(emissions).toEqual([true]); - }); - - it("forwards errors", () => { - const input$ = new Subject(); - const expected = { some: "error" }; - - let error = null; - anyComplete(input$).subscribe({ error: (e: unknown) => (error = e) }); - input$.error(expected); - - expect(error).toEqual(expected); - }); -}); - -describe("ready", () => { - it("connects when subscribed", () => { - const watch$ = new Subject(); - let connected = false; - const source$ = new Subject().pipe(tap({ subscribe: () => (connected = true) })); - - // precondition: ready$ should be cold - const ready$ = source$.pipe(ready(watch$)); - expect(connected).toBe(false); - - ready$.subscribe(); - - expect(connected).toBe(true); - }); - - it("suppresses source emissions until its watch emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - const results: number[] = []; - ready$.subscribe((n) => results.push(n)); - - // precondition: no emissions - source$.next(1); - expect(results).toEqual([]); - - watch$.next(); - - expect(results).toEqual([1]); - }); - - it("suppresses source emissions until all watches emit", () => { - const watchA$ = new Subject(); - const watchB$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready([watchA$, watchB$])); - const results: number[] = []; - ready$.subscribe((n) => results.push(n)); - - // preconditions: no emissions - source$.next(1); - expect(results).toEqual([]); - watchA$.next(); - expect(results).toEqual([]); - - watchB$.next(); - - expect(results).toEqual([1]); - }); - - it("emits the last source emission when its watch emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - const results: number[] = []; - ready$.subscribe((n) => results.push(n)); - - // precondition: no emissions - source$.next(1); - expect(results).toEqual([]); - - source$.next(2); - watch$.next(); - - expect(results).toEqual([2]); - }); - - it("emits all source emissions after its watch emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - const results: number[] = []; - ready$.subscribe((n) => results.push(n)); - - watch$.next(); - source$.next(1); - source$.next(2); - - expect(results).toEqual([1, 2]); - }); - - it("ignores repeated watch emissions", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - const results: number[] = []; - ready$.subscribe((n) => results.push(n)); - - watch$.next(); - source$.next(1); - watch$.next(); - source$.next(2); - watch$.next(); - - expect(results).toEqual([1, 2]); - }); - - it("completes when its source completes", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - let completed = false; - ready$.subscribe({ complete: () => (completed = true) }); - - source$.complete(); - - expect(completed).toBeTruthy(); - }); - - it("errors when its source errors", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - const expected = { some: "error" }; - let error = null; - ready$.subscribe({ error: (e: unknown) => (error = e) }); - - source$.error(expected); - - expect(error).toEqual(expected); - }); - - it("errors when its watch errors", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - const expected = { some: "error" }; - let error = null; - ready$.subscribe({ error: (e: unknown) => (error = e) }); - - watch$.error(expected); - - expect(error).toEqual(expected); - }); - - it("errors when its watch completes before emitting", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const ready$ = source$.pipe(ready(watch$)); - let error = null; - ready$.subscribe({ error: (e: unknown) => (error = e) }); - - watch$.complete(); - - expect(error).toBeInstanceOf(EmptyError); - }); -}); - -describe("on", () => { - it("connects when subscribed", () => { - const watch$ = new Subject(); - let connected = false; - const source$ = new Subject().pipe(tap({ subscribe: () => (connected = true) })); - - // precondition: on$ should be cold - const on$ = source$.pipe(on(watch$)); - expect(connected).toBeFalsy(); - - on$.subscribe(); - - expect(connected).toBeTruthy(); - }); - - it("suppresses source emissions until `on` emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const results: number[] = []; - source$.pipe(on(watch$)).subscribe((n) => results.push(n)); - - // precondition: on$ should be cold - source$.next(1); - expect(results).toEqual([]); - - watch$.next(); - - expect(results).toEqual([1]); - }); - - it("repeats source emissions when `on` emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const results: number[] = []; - source$.pipe(on(watch$)).subscribe((n) => results.push(n)); - source$.next(1); - - watch$.next(); - watch$.next(); - - expect(results).toEqual([1, 1]); - }); - - it("updates source emissions when `on` emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const results: number[] = []; - source$.pipe(on(watch$)).subscribe((n) => results.push(n)); - - source$.next(1); - watch$.next(); - source$.next(2); - watch$.next(); - - expect(results).toEqual([1, 2]); - }); - - it("emits a value when `on` emits before the source is ready", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const results: number[] = []; - source$.pipe(on(watch$)).subscribe((n) => results.push(n)); - - watch$.next(); - source$.next(1); - - expect(results).toEqual([1]); - }); - - it("ignores repeated `on` emissions before the source is ready", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const results: number[] = []; - source$.pipe(on(watch$)).subscribe((n) => results.push(n)); - - watch$.next(); - watch$.next(); - source$.next(1); - - expect(results).toEqual([1]); - }); - - it("emits only the latest source emission when `on` emits", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const results: number[] = []; - source$.pipe(on(watch$)).subscribe((n) => results.push(n)); - source$.next(1); - - watch$.next(); - - source$.next(2); - source$.next(3); - watch$.next(); - - expect(results).toEqual([1, 3]); - }); - - it("completes when its source completes", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - let complete: boolean = false; - source$.pipe(on(watch$)).subscribe({ complete: () => (complete = true) }); - - source$.complete(); - - expect(complete).toBeTruthy(); - }); - - it("completes when its watch completes", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - let complete: boolean = false; - source$.pipe(on(watch$)).subscribe({ complete: () => (complete = true) }); - - watch$.complete(); - - expect(complete).toBeTruthy(); - }); - - it("errors when its source errors", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const expected = { some: "error" }; - let error = null; - source$.pipe(on(watch$)).subscribe({ error: (e: unknown) => (error = e) }); - - source$.error(expected); - - expect(error).toEqual(expected); - }); - - it("errors when its watch errors", () => { - const watch$ = new Subject(); - const source$ = new Subject(); - const expected = { some: "error" }; - let error = null; - source$.pipe(on(watch$)).subscribe({ error: (e: unknown) => (error = e) }); - - watch$.error(expected); - - expect(error).toEqual(expected); - }); -}); diff --git a/libs/tools/generator/core/src/rx.ts b/libs/tools/generator/core/src/rx.ts index 851b6cfe7c7..070d34d37d8 100644 --- a/libs/tools/generator/core/src/rx.ts +++ b/libs/tools/generator/core/src/rx.ts @@ -1,18 +1,4 @@ -import { - concat, - concatMap, - connect, - endWith, - first, - ignoreElements, - map, - Observable, - pipe, - race, - ReplaySubject, - takeUntil, - zip, -} from "rxjs"; +import { map, pipe } from "rxjs"; import { reduceCollection, distinctIfShallowMatch } from "@bitwarden/common/tools/rx"; @@ -51,86 +37,3 @@ export function newDefaultEvaluator() { return pipe(map((_) => new DefaultPolicyEvaluator())); }; } - -/** Create an observable that, once subscribed, emits `true` then completes when - * any input completes. If an input is already complete when the subscription - * occurs, it emits immediately. - * @param watch$ the observable(s) to watch for completion; if an array is passed, - * null and undefined members are ignored. If `watch$` is empty, `anyComplete` - * will never complete. - * @returns An observable that emits `true` when any of its inputs - * complete. The observable forwards the first error from its input. - * @remarks This method is particularly useful in combination with `takeUntil` and - * streams that are not guaranteed to complete on their own. - */ -export function anyComplete(watch$: Observable | Observable[]): Observable { - if (Array.isArray(watch$)) { - const completes$ = watch$ - .filter((w$) => !!w$) - .map((w$) => w$.pipe(ignoreElements(), endWith(true))); - const completed$ = race(completes$); - return completed$; - } else { - return watch$.pipe(ignoreElements(), endWith(true)); - } -} - -/** - * Create an observable that delays the input stream until all watches have - * emitted a value. The watched values are not included in the source stream. - * The last emission from the source is output when all the watches have - * emitted at least once. - * @param watch$ the observable(s) to watch for readiness. If `watch$` is empty, - * `ready` will never emit. - * @returns An observable that emits when the source stream emits. The observable - * errors if one of its watches completes before emitting. It also errors if one - * of its watches errors. - */ -export function ready(watch$: Observable | Observable[]) { - const watching$ = Array.isArray(watch$) ? watch$ : [watch$]; - return pipe( - connect>((source$) => { - // this subscription is safe because `source$` connects only after there - // is an external subscriber. - const source = new ReplaySubject(1); - source$.subscribe(source); - - // `concat` is subscribed immediately after it's returned, at which point - // `zip` blocks until all items in `watching$` are ready. If that occurs - // after `source$` is hot, then the replay subject sends the last-captured - // emission through immediately. Otherwise, `ready` waits for the next - // emission - return concat(zip(watching$).pipe(first(), ignoreElements()), source).pipe( - takeUntil(anyComplete(source)), - ); - }), - ); -} - -/** - * Create an observable that emits the latest value of the source stream - * when `watch$` emits. If `watch$` emits before the stream emits, then - * an emission occurs as soon as a value becomes ready. - * @param watch$ the observable that triggers emissions - * @returns An observable that emits when `watch$` emits. The observable - * errors if its source stream errors. It also errors if `on` errors. It - * completes if its watch completes. - * - * @remarks This works like `audit`, but it repeats emissions when - * watch$ fires. - */ -export function on(watch$: Observable) { - return pipe( - connect>((source$) => { - const source = new ReplaySubject(1); - source$.subscribe(source); - - return watch$ - .pipe( - ready(source), - concatMap(() => source.pipe(first())), - ) - .pipe(takeUntil(anyComplete(source))); - }), - ); -} diff --git a/libs/tools/generator/core/src/services/credential-generator.service.spec.ts b/libs/tools/generator/core/src/services/credential-generator.service.spec.ts index 88f1447e98d..e11e555d6aa 100644 --- a/libs/tools/generator/core/src/services/credential-generator.service.spec.ts +++ b/libs/tools/generator/core/src/services/credential-generator.service.spec.ts @@ -1,12 +1,17 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, filter, firstValueFrom, Subject } from "rxjs"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; +import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/state"; import { StateConstraints } from "@bitwarden/common/tools/types"; import { OrganizationId, PolicyId, UserId } from "@bitwarden/common/types/guid"; +import { UserKey } from "@bitwarden/common/types/key"; import { FakeStateProvider, @@ -67,15 +72,20 @@ const SomeTime = new Date(1); const SomeAlgorithm = "passphrase"; const SomeCategory = "password"; const SomeNameKey = "passphraseKey"; +const SomeGenerateKey = "generateKey"; +const SomeCopyKey = "copyKey"; // fake the configuration const SomeConfiguration: CredentialGeneratorConfiguration = { id: SomeAlgorithm, category: SomeCategory, nameKey: SomeNameKey, + generateKey: SomeGenerateKey, + copyKey: SomeCopyKey, onlyOnRequest: false, + request: [], engine: { - create: (randomizer) => { + create: (_randomizer) => { return { generate: (request, settings) => { const credential = request.website ? `${request.website}|${settings.foo}` : settings.foo; @@ -159,10 +169,22 @@ const stateProvider = new FakeStateProvider(accountService); // fake randomizer const randomizer = mock(); +const i18nService = mock(); + +const apiService = mock(); + +const encryptService = mock(); + +const cryptoService = mock(); + describe("CredentialGeneratorService", () => { beforeEach(async () => { await accountService.switchAccount(SomeUser); policyService.getAll$.mockImplementation(() => new BehaviorSubject([]).asObservable()); + i18nService.t.mockImplementation((key) => key); + apiService.fetch.mockImplementation(() => Promise.resolve(mock())); + const keyAvailable = new BehaviorSubject({} as UserKey); + cryptoService.userKey$.mockReturnValue(keyAvailable); jest.clearAllMocks(); }); @@ -170,7 +192,15 @@ describe("CredentialGeneratorService", () => { it("emits a generation for the active user when subscribed", async () => { const settings = { foo: "value" }; await stateProvider.setUserState(SettingsKey, settings, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); const result = await generated.expectEmission(); @@ -183,7 +213,15 @@ describe("CredentialGeneratorService", () => { const anotherSettings = { foo: "another value" }; await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); await accountService.switchAccount(AnotherUser); @@ -200,7 +238,15 @@ describe("CredentialGeneratorService", () => { const someSettings = { foo: "some value" }; const anotherSettings = { foo: "another value" }; await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const generated = new ObservableTracker(generator.generate$(SomeConfiguration)); await stateProvider.setUserState(SettingsKey, anotherSettings, SomeUser); @@ -220,7 +266,15 @@ describe("CredentialGeneratorService", () => { it("includes `website$`'s last emitted value", async () => { const settings = { foo: "value" }; await stateProvider.setUserState(SettingsKey, settings, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const website$ = new BehaviorSubject("some website"); const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { website$ })); @@ -233,7 +287,15 @@ describe("CredentialGeneratorService", () => { it("errors when `website$` errors", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const website$ = new BehaviorSubject("some website"); let error = null; @@ -250,7 +312,15 @@ describe("CredentialGeneratorService", () => { it("completes when `website$` completes", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const website$ = new BehaviorSubject("some website"); let completed = false; @@ -268,7 +338,15 @@ describe("CredentialGeneratorService", () => { it("emits a generation for a specific user when `user$` supplied", async () => { await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); await stateProvider.setUserState(SettingsKey, { foo: "another" }, AnotherUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { userId$ })); @@ -280,7 +358,15 @@ describe("CredentialGeneratorService", () => { it("emits a generation for a specific user when `user$` emits", async () => { await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); await stateProvider.setUserState(SettingsKey, { foo: "another" }, AnotherUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.pipe(filter((u) => !!u)); const generated = new ObservableTracker(generator.generate$(SomeConfiguration, { userId$ })); @@ -296,7 +382,15 @@ describe("CredentialGeneratorService", () => { it("errors when `user$` errors", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(SomeUser); let error = null; @@ -313,7 +407,15 @@ describe("CredentialGeneratorService", () => { it("completes when `user$` completes", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(SomeUser); let completed = false; @@ -331,7 +433,15 @@ describe("CredentialGeneratorService", () => { it("emits a generation only when `on$` emits", async () => { // This test breaks from arrange/act/assert because it is testing causality await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const on$ = new Subject(); const results: any[] = []; @@ -365,7 +475,15 @@ describe("CredentialGeneratorService", () => { it("errors when `on$` errors", async () => { await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const on$ = new Subject(); let error: any = null; @@ -383,7 +501,15 @@ describe("CredentialGeneratorService", () => { it("completes when `on$` completes", async () => { await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const on$ = new Subject(); let complete = false; @@ -406,54 +532,86 @@ describe("CredentialGeneratorService", () => { describe("algorithms", () => { it("outputs password generation metadata", () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = generator.algorithms("password"); - expect(result).toContain(Generators.password); - expect(result).toContain(Generators.passphrase); + expect(result.some((a) => a.id === Generators.password.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.passphrase.id)).toBeTruthy(); // this test shouldn't contain entries outside of the current category - expect(result).not.toContain(Generators.username); - expect(result).not.toContain(Generators.catchall); + expect(result.some((a) => a.id === Generators.username.id)).toBeFalsy(); + expect(result.some((a) => a.id === Generators.catchall.id)).toBeFalsy(); }); it("outputs username generation metadata", () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = generator.algorithms("username"); - expect(result).toContain(Generators.username); + expect(result.some((a) => a.id === Generators.username.id)).toBeTruthy(); // this test shouldn't contain entries outside of the current category - expect(result).not.toContain(Generators.catchall); - expect(result).not.toContain(Generators.password); + expect(result.some((a) => a.id === Generators.catchall.id)).toBeFalsy(); + expect(result.some((a) => a.id === Generators.password.id)).toBeFalsy(); }); it("outputs email generation metadata", () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = generator.algorithms("email"); - expect(result).toContain(Generators.catchall); - expect(result).toContain(Generators.subaddress); + expect(result.some((a) => a.id === Generators.catchall.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.subaddress.id)).toBeTruthy(); // this test shouldn't contain entries outside of the current category - expect(result).not.toContain(Generators.username); - expect(result).not.toContain(Generators.password); + expect(result.some((a) => a.id === Generators.username.id)).toBeFalsy(); + expect(result.some((a) => a.id === Generators.password.id)).toBeFalsy(); }); it("combines metadata across categories", () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = generator.algorithms(["username", "email"]); - expect(result).toContain(Generators.username); - expect(result).toContain(Generators.catchall); - expect(result).toContain(Generators.subaddress); + expect(result.some((a) => a.id === Generators.username.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.catchall.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.subaddress.id)).toBeTruthy(); // this test shouldn't contain entries outside of the current categories - expect(result).not.toContain(Generators.password); + expect(result.some((a) => a.id === Generators.password.id)).toBeFalsy(); }); }); @@ -461,39 +619,71 @@ describe("CredentialGeneratorService", () => { // these tests cannot use the observable tracker because they return // data that cannot be cloned it("returns password metadata", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.algorithms$("password")); - expect(result).toContain(Generators.password); - expect(result).toContain(Generators.passphrase); + expect(result.some((a) => a.id === Generators.password.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.passphrase.id)).toBeTruthy(); }); it("returns username metadata", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.algorithms$("username")); - expect(result).toContain(Generators.username); + expect(result.some((a) => a.id === Generators.username.id)).toBeTruthy(); }); it("returns email metadata", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.algorithms$("email")); - expect(result).toContain(Generators.catchall); - expect(result).toContain(Generators.subaddress); + expect(result.some((a) => a.id === Generators.catchall.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.subaddress.id)).toBeTruthy(); }); it("returns username and email metadata", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.algorithms$(["username", "email"])); - expect(result).toContain(Generators.username); - expect(result).toContain(Generators.catchall); - expect(result).toContain(Generators.subaddress); + expect(result.some((a) => a.id === Generators.username.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.catchall.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.subaddress.id)).toBeTruthy(); }); // Subsequent tests focus on passwords and passphrases as an example of policy @@ -501,13 +691,21 @@ describe("CredentialGeneratorService", () => { it("enforces the active user's policy", async () => { const policy$ = new BehaviorSubject([passwordOverridePolicy]); policyService.getAll$.mockReturnValue(policy$); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.algorithms$(["password"])); expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, SomeUser); - expect(result).toContain(Generators.password); - expect(result).not.toContain(Generators.passphrase); + expect(result.some((a) => a.id === Generators.password.id)).toBeTruthy(); + expect(result.some((a) => a.id === Generators.passphrase.id)).toBeFalsy(); }); it("follows changes to the active user", async () => { @@ -518,7 +716,15 @@ describe("CredentialGeneratorService", () => { await accountService.switchAccount(SomeUser); policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy])); policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passphraseOverridePolicy])); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const results: any = []; const sub = generator.algorithms$("password").subscribe((r) => results.push(r)); @@ -533,34 +739,50 @@ describe("CredentialGeneratorService", () => { PolicyType.PasswordGenerator, SomeUser, ); - expect(someResult).toContain(Generators.password); - expect(someResult).not.toContain(Generators.passphrase); + expect(someResult.some((a: any) => a.id === Generators.password.id)).toBeTruthy(); + expect(someResult.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy(); expect(policyService.getAll$).toHaveBeenNthCalledWith( 2, PolicyType.PasswordGenerator, AnotherUser, ); - expect(anotherResult).toContain(Generators.passphrase); - expect(anotherResult).not.toContain(Generators.password); + expect(anotherResult.some((a: any) => a.id === Generators.passphrase.id)).toBeTruthy(); + expect(anotherResult.some((a: any) => a.id === Generators.password.id)).toBeFalsy(); }); it("reads an arbitrary user's settings", async () => { policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy])); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); const result = await firstValueFrom(generator.algorithms$("password", { userId$ })); expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, AnotherUser); - expect(result).toContain(Generators.password); - expect(result).not.toContain(Generators.passphrase); + expect(result.some((a: any) => a.id === Generators.password.id)).toBeTruthy(); + expect(result.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy(); }); it("follows changes to the arbitrary user", async () => { policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy])); policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passphraseOverridePolicy])); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const results: any = []; @@ -572,17 +794,25 @@ describe("CredentialGeneratorService", () => { const [someResult, anotherResult] = results; expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, SomeUser); - expect(someResult).toContain(Generators.password); - expect(someResult).not.toContain(Generators.passphrase); + expect(someResult.some((a: any) => a.id === Generators.password.id)).toBeTruthy(); + expect(someResult.some((a: any) => a.id === Generators.passphrase.id)).toBeFalsy(); expect(policyService.getAll$).toHaveBeenCalledWith(PolicyType.PasswordGenerator, AnotherUser); - expect(anotherResult).toContain(Generators.passphrase); - expect(anotherResult).not.toContain(Generators.password); + expect(anotherResult.some((a: any) => a.id === Generators.passphrase.id)).toBeTruthy(); + expect(anotherResult.some((a: any) => a.id === Generators.password.id)).toBeFalsy(); }); it("errors when the arbitrary user's stream errors", async () => { policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy])); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let error = null; @@ -600,7 +830,15 @@ describe("CredentialGeneratorService", () => { it("completes when the arbitrary user's stream completes", async () => { policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy])); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let completed = false; @@ -618,7 +856,15 @@ describe("CredentialGeneratorService", () => { it("ignores repeated arbitrary user emissions", async () => { policyService.getAll$.mockReturnValueOnce(new BehaviorSubject([passwordOverridePolicy])); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let count = 0; @@ -642,7 +888,15 @@ describe("CredentialGeneratorService", () => { describe("settings$", () => { it("defaults to the configuration's initial settings if settings aren't found", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -652,7 +906,15 @@ describe("CredentialGeneratorService", () => { it("reads from the active user's configuration-defined storage", async () => { const settings = { foo: "value" }; await stateProvider.setUserState(SettingsKey, settings, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -664,7 +926,15 @@ describe("CredentialGeneratorService", () => { await stateProvider.setUserState(SettingsKey, settings, SomeUser); const policy$ = new BehaviorSubject([somePolicy]); policyService.getAll$.mockReturnValue(policy$); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const result = await firstValueFrom(generator.settings$(SomeConfiguration)); @@ -672,7 +942,7 @@ describe("CredentialGeneratorService", () => { }); it("follows changes to the active user", async () => { - // initialize local accound service and state provider because this test is sensitive + // initialize local account service and state provider because this test is sensitive // to some shared data in `FakeAccountService`. const accountService = new FakeAccountService(accounts); const stateProvider = new FakeStateProvider(accountService); @@ -681,7 +951,15 @@ describe("CredentialGeneratorService", () => { const anotherSettings = { foo: "another" }; await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const results: any = []; const sub = generator.settings$(SomeConfiguration).subscribe((r) => results.push(r)); @@ -698,7 +976,15 @@ describe("CredentialGeneratorService", () => { await stateProvider.setUserState(SettingsKey, { foo: "value" }, SomeUser); const anotherSettings = { foo: "another" }; await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(AnotherUser).asObservable(); const result = await firstValueFrom(generator.settings$(SomeConfiguration, { userId$ })); @@ -711,7 +997,15 @@ describe("CredentialGeneratorService", () => { await stateProvider.setUserState(SettingsKey, someSettings, SomeUser); const anotherSettings = { foo: "another" }; await stateProvider.setUserState(SettingsKey, anotherSettings, AnotherUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const results: any = []; @@ -730,7 +1024,15 @@ describe("CredentialGeneratorService", () => { it("errors when the arbitrary user's stream errors", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let error = null; @@ -748,7 +1050,15 @@ describe("CredentialGeneratorService", () => { it("completes when the arbitrary user's stream completes", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let completed = false; @@ -766,7 +1076,15 @@ describe("CredentialGeneratorService", () => { it("ignores repeated arbitrary user emissions", async () => { await stateProvider.setUserState(SettingsKey, null, SomeUser); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); let count = 0; @@ -790,7 +1108,15 @@ describe("CredentialGeneratorService", () => { describe("settings", () => { it("writes to the user's state", async () => { const singleUserId$ = new BehaviorSubject(SomeUser).asObservable(); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const subject = await generator.settings(SomeConfiguration, { singleUserId$ }); subject.next({ foo: "next value" }); @@ -803,7 +1129,15 @@ describe("CredentialGeneratorService", () => { it("waits for the user to become available", async () => { const singleUserId = new BehaviorSubject(null); const singleUserId$ = singleUserId.asObservable(); - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); let completed = false; const promise = generator.settings(SomeConfiguration, { singleUserId$ }).then((settings) => { @@ -821,7 +1155,15 @@ describe("CredentialGeneratorService", () => { describe("policy$", () => { it("creates constraints without policy in effect when there is no policy", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(SomeUser).asObservable(); const result = await firstValueFrom(generator.policy$(SomeConfiguration, { userId$ })); @@ -830,7 +1172,15 @@ describe("CredentialGeneratorService", () => { }); it("creates constraints with policy in effect when there is a policy", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId$ = new BehaviorSubject(SomeUser).asObservable(); const policy$ = new BehaviorSubject([somePolicy]); policyService.getAll$.mockReturnValue(policy$); @@ -841,7 +1191,15 @@ describe("CredentialGeneratorService", () => { }); it("follows policy emissions", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const somePolicySubject = new BehaviorSubject([somePolicy]); @@ -862,7 +1220,15 @@ describe("CredentialGeneratorService", () => { }); it("follows user emissions", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const somePolicy$ = new BehaviorSubject([somePolicy]).asObservable(); @@ -884,7 +1250,15 @@ describe("CredentialGeneratorService", () => { }); it("errors when the user errors", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); const expectedError = { some: "error" }; @@ -902,7 +1276,15 @@ describe("CredentialGeneratorService", () => { }); it("completes when the user completes", async () => { - const generator = new CredentialGeneratorService(randomizer, stateProvider, policyService); + const generator = new CredentialGeneratorService( + randomizer, + stateProvider, + policyService, + apiService, + i18nService, + encryptService, + cryptoService, + ); const userId = new BehaviorSubject(SomeUser); const userId$ = userId.asObservable(); diff --git a/libs/tools/generator/core/src/services/credential-generator.service.ts b/libs/tools/generator/core/src/services/credential-generator.service.ts index 693ffd654dc..a137c153a64 100644 --- a/libs/tools/generator/core/src/services/credential-generator.service.ts +++ b/libs/tools/generator/core/src/services/credential-generator.service.ts @@ -11,38 +11,60 @@ import { ignoreElements, map, Observable, - race, share, skipUntil, switchMap, takeUntil, + takeWhile, withLatestFrom, } from "rxjs"; import { Simplify } from "type-fest"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { OnDependency, SingleUserDependency, + UserBound, UserDependency, } from "@bitwarden/common/tools/dependencies"; -import { isDynamic } from "@bitwarden/common/tools/state/state-constraints-dependency"; +import { IntegrationId, IntegrationMetadata } from "@bitwarden/common/tools/integration"; +import { RestClient } from "@bitwarden/common/tools/integration/rpc"; +import { anyComplete } from "@bitwarden/common/tools/rx"; +import { PaddedDataPacker } from "@bitwarden/common/tools/state/padded-data-packer"; +import { UserEncryptor } from "@bitwarden/common/tools/state/user-encryptor.abstraction"; +import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor"; import { UserStateSubject } from "@bitwarden/common/tools/state/user-state-subject"; +import { UserId } from "@bitwarden/common/types/guid"; import { Randomizer } from "../abstractions"; -import { Generators } from "../data"; +import { + Generators, + getForwarderConfiguration, + Integrations, + toCredentialGeneratorConfiguration, +} from "../data"; import { availableAlgorithms } from "../policies/available-algorithms-policy"; import { mapPolicyToConstraints } from "../rx"; import { CredentialAlgorithm, CredentialCategories, CredentialCategory, - CredentialGeneratorInfo, + AlgorithmInfo, CredentialPreference, + isForwarderIntegration, + ForwarderIntegration, } from "../types"; -import { CredentialGeneratorConfiguration as Configuration } from "../types/credential-generator-configuration"; +import { + CredentialGeneratorConfiguration as Configuration, + CredentialGeneratorInfo, + GeneratorDependencyProvider, +} from "../types/credential-generator-configuration"; import { GeneratorConstraints } from "../types/generator-constraints"; import { PREFERENCES } from "./credential-preferences"; @@ -59,17 +81,33 @@ type Generate$Dependencies = Simplify & Partial; + + integration$?: Observable; }; type Algorithms$Dependencies = Partial; +const OPTIONS_FRAME_SIZE = 512; + export class CredentialGeneratorService { constructor( - private randomizer: Randomizer, - private stateProvider: StateProvider, - private policyService: PolicyService, + private readonly randomizer: Randomizer, + private readonly stateProvider: StateProvider, + private readonly policyService: PolicyService, + private readonly apiService: ApiService, + private readonly i18nService: I18nService, + private readonly encryptService: EncryptService, + private readonly cryptoService: CryptoService, ) {} + private getDependencyProvider(): GeneratorDependencyProvider { + return { + client: new RestClient(this.apiService, this.i18nService), + i18nService: this.i18nService, + randomizer: this.randomizer, + }; + } + // FIXME: the rxjs methods of this service can be a lot more resilient if // `Subjects` are introduced where sharing occurs @@ -84,18 +122,13 @@ export class CredentialGeneratorService { dependencies?: Generate$Dependencies, ) { // instantiate the engine - const engine = configuration.engine.create(this.randomizer); + const engine = configuration.engine.create(this.getDependencyProvider()); // stream blocks until all of these values are received const website$ = dependencies?.website$ ?? new BehaviorSubject(null); const request$ = website$.pipe(map((website) => ({ website }))); const settings$ = this.settings$(configuration, dependencies); - // monitor completion - const requestComplete$ = request$.pipe(ignoreElements(), endWith(true)); - const settingsComplete$ = request$.pipe(ignoreElements(), endWith(true)); - const complete$ = race(requestComplete$, settingsComplete$); - // if on$ triggers before settings are loaded, trigger as soon // as they become available. let readyOn$: Observable = null; @@ -116,7 +149,7 @@ export class CredentialGeneratorService { const generate$ = (readyOn$ ?? settings$).pipe( withLatestFrom(request$, settings$), concatMap(([, request, settings]) => engine.generate(request, settings)), - takeUntil(complete$), + takeUntil(anyComplete([request$, settings$])), ); return generate$; @@ -132,11 +165,11 @@ export class CredentialGeneratorService { algorithms$( category: CredentialCategory, dependencies?: Algorithms$Dependencies, - ): Observable; + ): Observable; algorithms$( category: CredentialCategory[], dependencies?: Algorithms$Dependencies, - ): Observable; + ): Observable; algorithms$( category: CredentialCategory | CredentialCategory[], dependencies?: Algorithms$Dependencies, @@ -163,7 +196,9 @@ export class CredentialGeneratorService { return policies$; }), map((available) => { - const filtered = algorithms.filter((c) => available.has(c.id)); + const filtered = algorithms.filter( + (c) => isForwarderIntegration(c.id) || available.has(c.id), + ); return filtered; }), ); @@ -175,24 +210,79 @@ export class CredentialGeneratorService { * @param category the category or categories of interest * @returns A list containing the requested metadata. */ - algorithms(category: CredentialCategory): CredentialGeneratorInfo[]; - algorithms(category: CredentialCategory[]): CredentialGeneratorInfo[]; - algorithms(category: CredentialCategory | CredentialCategory[]): CredentialGeneratorInfo[] { - const categories = Array.isArray(category) ? category : [category]; + algorithms(category: CredentialCategory): AlgorithmInfo[]; + algorithms(category: CredentialCategory[]): AlgorithmInfo[]; + algorithms(category: CredentialCategory | CredentialCategory[]): AlgorithmInfo[] { + const categories: CredentialCategory[] = Array.isArray(category) ? category : [category]; + const algorithms = categories - .flatMap((c) => CredentialCategories[c]) - .map((c) => (c === "forwarder" ? null : Generators[c])) + .flatMap((c) => CredentialCategories[c] as CredentialAlgorithm[]) + .map((id) => this.algorithm(id)) .filter((info) => info !== null); - return algorithms; + const forwarders = Object.keys(Integrations) + .map((key: keyof typeof Integrations) => { + const forwarder: ForwarderIntegration = { forwarder: Integrations[key].id }; + return this.algorithm(forwarder); + }) + .filter((forwarder) => categories.includes(forwarder.category)); + + return algorithms.concat(forwarders); } /** Look up the metadata for a specific generator algorithm * @param id identifies the algorithm * @returns the requested metadata, or `null` if the metadata wasn't found. */ - algorithm(id: CredentialAlgorithm): CredentialGeneratorInfo { - return (id === "forwarder" ? null : Generators[id]) ?? null; + algorithm(id: CredentialAlgorithm): AlgorithmInfo { + let generator: CredentialGeneratorInfo = null; + let integration: IntegrationMetadata = null; + + if (isForwarderIntegration(id)) { + const forwarderConfig = getForwarderConfiguration(id.forwarder); + integration = forwarderConfig; + + if (forwarderConfig) { + generator = toCredentialGeneratorConfiguration(forwarderConfig); + } + } else { + generator = Generators[id]; + } + + if (!generator) { + throw new Error(`Invalid credential algorithm: ${JSON.stringify(id)}`); + } + + const info: AlgorithmInfo = { + id: generator.id, + category: generator.category, + name: integration ? integration.name : this.i18nService.t(generator.nameKey), + generate: this.i18nService.t(generator.generateKey), + copy: this.i18nService.t(generator.copyKey), + onlyOnRequest: generator.onlyOnRequest, + request: generator.request, + }; + + if (generator.descriptionKey) { + info.description = this.i18nService.t(generator.descriptionKey); + } + + return info; + } + + private encryptor$(userId: UserId) { + const packer = new PaddedDataPacker(OPTIONS_FRAME_SIZE); + const encryptor$ = this.cryptoService.userKey$(userId).pipe( + // complete when the account locks + takeWhile((key) => !!key), + map((key) => { + const encryptor = new UserKeyEncryptor(userId, this.encryptService, key, packer); + + return { userId, encryptor } satisfies UserBound<"encryptor", UserEncryptor>; + }), + ); + + return encryptor$; } /** Get the settings for the provided configuration @@ -208,27 +298,21 @@ export class CredentialGeneratorService { dependencies?: Settings$Dependencies, ) { const userId$ = dependencies?.userId$ ?? this.stateProvider.activeUserId$; - const completion$ = userId$.pipe(ignoreElements(), endWith(true)); + const constraints$ = this.policy$(configuration, { userId$ }); - const state$ = userId$.pipe( + const settings$ = userId$.pipe( filter((userId) => !!userId), distinctUntilChanged(), switchMap((userId) => { - const state$ = this.stateProvider - .getUserState$(configuration.settings.account, userId) - .pipe(takeUntil(completion$)); - + const state$ = new UserStateSubject( + configuration.settings.account, + (key) => this.stateProvider.getUser(userId, key), + { constraints$, singleUserEncryptor$: this.encryptor$(userId) }, + ); return state$; }), map((settings) => settings ?? structuredClone(configuration.settings.initial)), - ); - - const settings$ = combineLatest([state$, this.policy$(configuration, { userId$ })]).pipe( - map(([settings, policy]) => { - const calibration = isDynamic(policy) ? policy.calibrate(settings) : policy; - const adjusted = calibration.adjust(settings); - return adjusted; - }), + takeUntil(anyComplete(userId$)), ); return settings$; @@ -251,8 +335,11 @@ export class CredentialGeneratorService { ); // FIXME: enforce policy - const state = this.stateProvider.getUser(userId, PREFERENCES); - const subject = new UserStateSubject(state, { ...dependencies }); + const subject = new UserStateSubject( + PREFERENCES, + (key) => this.stateProvider.getUser(userId, key), + { singleUserEncryptor$: this.encryptor$(userId) }, + ); return subject; } @@ -271,10 +358,14 @@ export class CredentialGeneratorService { const userId = await firstValueFrom( dependencies.singleUserId$.pipe(filter((userId) => !!userId)), ); - const state = this.stateProvider.getUser(userId, configuration.settings.account); + const constraints$ = this.policy$(configuration, { userId$: dependencies.singleUserId$ }); - const subject = new UserStateSubject(state, { ...dependencies, constraints$ }); + const subject = new UserStateSubject( + configuration.settings.account, + (key) => this.stateProvider.getUser(userId, key), + { constraints$, singleUserEncryptor$: this.encryptor$(userId) }, + ); return subject; } diff --git a/libs/tools/generator/core/src/types/credential-generator-configuration.ts b/libs/tools/generator/core/src/types/credential-generator-configuration.ts index 8302450d443..1798323ec63 100644 --- a/libs/tools/generator/core/src/types/credential-generator-configuration.ts +++ b/libs/tools/generator/core/src/types/credential-generator-configuration.ts @@ -1,4 +1,7 @@ +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { UserKeyDefinition } from "@bitwarden/common/platform/state"; +import { RestClient } from "@bitwarden/common/tools/integration/rpc"; +import { ObjectKey } from "@bitwarden/common/tools/state/object-key"; import { Constraints } from "@bitwarden/common/tools/types"; import { Randomizer } from "../abstractions"; @@ -6,9 +9,58 @@ import { CredentialAlgorithm, CredentialCategory, PolicyConfiguration } from ".. import { CredentialGenerator } from "./credential-generator"; +export type GeneratorDependencyProvider = { + randomizer: Randomizer; + client: RestClient; + i18nService: I18nService; +}; + +export type AlgorithmInfo = { + /** Uniquely identifies the credential configuration + * @example + * // Use `isForwarderIntegration(algorithm: CredentialAlgorithm)` + * // to pattern test whether the credential describes a forwarder algorithm + * const meta : CredentialGeneratorInfo = // ... + * const { forwarder } = isForwarderIntegration(meta.id) ? credentialId : {}; + */ + id: CredentialAlgorithm; + + /** The kind of credential generated by this configuration */ + category: CredentialCategory; + + /** Localized algorithm name */ + name: string; + + /* Localized generate button label */ + generate: string; + + /* Localized copy button label */ + copy: string; + + /** Localized algorithm description */ + description?: string; + + /** When true, credential generation must be explicitly requested. + * @remarks this property is useful when credential generation + * carries side effects, such as configuring a service external + * to Bitwarden. + */ + onlyOnRequest: boolean; + + /** Well-known fields to display on the options panel or collect from the environment. + * @remarks: at present, this is only used by forwarders + */ + request: readonly string[]; +}; + /** Credential generator metadata common across credential generators */ export type CredentialGeneratorInfo = { /** Uniquely identifies the credential configuration + * @example + * // Use `isForwarderIntegration(algorithm: CredentialAlgorithm)` + * // to pattern test whether the credential describes a forwarder algorithm + * const meta : CredentialGeneratorInfo = // ... + * const { forwarder } = isForwarderIntegration(meta.id) ? credentialId : {}; */ id: CredentialAlgorithm; @@ -21,15 +73,32 @@ export type CredentialGeneratorInfo = { /** Key used to localize the credential description in the I18nService */ descriptionKey?: string; + /* Localized generate button label */ + generateKey: string; + + /* Localized copy button label */ + copyKey: string; + /** When true, credential generation must be explicitly requested. * @remarks this property is useful when credential generation * carries side effects, such as configuring a service external * to Bitwarden. */ onlyOnRequest: boolean; + + /** Well-known fields to display on the options panel or collect from the environment. + * @remarks: at present, this is only used by forwarders + */ + request: readonly string[]; }; -/** Credential generator metadata that relies upon typed setting and policy definitions. */ +/** Credential generator metadata that relies upon typed setting and policy definitions. + * @example + * // Use `isForwarderIntegration(algorithm: CredentialAlgorithm)` + * // to pattern test whether the credential describes a forwarder algorithm + * const meta : CredentialGeneratorInfo = // ... + * const { forwarder } = isForwarderIntegration(meta.id) ? credentialId : {}; + */ export type CredentialGeneratorConfiguration = CredentialGeneratorInfo & { /** An algorithm that generates credentials when ran. */ engine: { @@ -40,7 +109,7 @@ export type CredentialGeneratorConfiguration = CredentialGener // the credential generator, but engine configurations should return // the underlying type. `create` may be able to do double-duty w/ an // engine definition if `CredentialGenerator` can be made covariant. - create: (randomizer: Randomizer) => CredentialGenerator; + create: (randomizer: GeneratorDependencyProvider) => CredentialGenerator; }; /** Defines the stored parameters for credential generation */ settings: { @@ -51,7 +120,10 @@ export type CredentialGeneratorConfiguration = CredentialGener constraints: Constraints; /** storage location for account-global settings */ - account: UserKeyDefinition; + account: UserKeyDefinition | ObjectKey; + + /** storage location for *plaintext* settings imports */ + import?: UserKeyDefinition | ObjectKey, Settings>; }; /** defines how to construct policy for this settings instance */ diff --git a/libs/tools/generator/core/src/types/generator-type.ts b/libs/tools/generator/core/src/types/generator-type.ts index 59727fb98f2..5b74d17fa4a 100644 --- a/libs/tools/generator/core/src/types/generator-type.ts +++ b/libs/tools/generator/core/src/types/generator-type.ts @@ -1,3 +1,5 @@ +import { IntegrationId } from "@bitwarden/common/tools/integration"; + import { EmailAlgorithms, PasswordAlgorithms, UsernameAlgorithms } from "../data/generator-types"; /** A type of password that may be generated by the credential generator. */ @@ -9,8 +11,31 @@ export type UsernameAlgorithm = (typeof UsernameAlgorithms)[number]; /** A type of email address that may be generated by the credential generator. */ export type EmailAlgorithm = (typeof EmailAlgorithms)[number]; +export type ForwarderIntegration = { forwarder: IntegrationId }; + +/** Returns true when the input algorithm is a forwarder integration. */ +export function isForwarderIntegration( + algorithm: CredentialAlgorithm, +): algorithm is ForwarderIntegration { + return algorithm && typeof algorithm === "object" && "forwarder" in algorithm; +} + +export function isSameAlgorithm(lhs: CredentialAlgorithm, rhs: CredentialAlgorithm) { + if (lhs === rhs) { + return true; + } else if (isForwarderIntegration(lhs) && isForwarderIntegration(rhs)) { + return lhs.forwarder === rhs.forwarder; + } else { + return false; + } +} + /** A type of credential that may be generated by the credential generator. */ -export type CredentialAlgorithm = PasswordAlgorithm | UsernameAlgorithm | EmailAlgorithm; +export type CredentialAlgorithm = + | PasswordAlgorithm + | UsernameAlgorithm + | EmailAlgorithm + | ForwarderIntegration; /** Compound credential types supported by the credential generator. */ export const CredentialCategories = Object.freeze({ @@ -21,7 +46,7 @@ export const CredentialCategories = Object.freeze({ username: UsernameAlgorithms as Readonly, /** Lists algorithms in the "email" credential category */ - email: EmailAlgorithms as Readonly, + email: EmailAlgorithms as Readonly<(EmailAlgorithm | ForwarderIntegration)[]>, }); /** Returns true when the input algorithm is a password algorithm. */ @@ -40,7 +65,7 @@ export function isUsernameAlgorithm( /** Returns true when the input algorithm is an email algorithm. */ export function isEmailAlgorithm(algorithm: CredentialAlgorithm): algorithm is EmailAlgorithm { - return EmailAlgorithms.includes(algorithm as any); + return EmailAlgorithms.includes(algorithm as any) || isForwarderIntegration(algorithm); } /** A type of compound credential that may be generated by the credential generator. */ diff --git a/libs/tools/generator/core/src/types/index.ts b/libs/tools/generator/core/src/types/index.ts index 884d9760078..48272cbf602 100644 --- a/libs/tools/generator/core/src/types/index.ts +++ b/libs/tools/generator/core/src/types/index.ts @@ -1,4 +1,4 @@ -import { CredentialAlgorithm, PasswordAlgorithm } from "./generator-type"; +import { EmailAlgorithm, PasswordAlgorithm, UsernameAlgorithm } from "./generator-type"; export * from "./boundary"; export * from "./catchall-generator-options"; @@ -22,7 +22,7 @@ export * from "./word-options"; /** Provided for backwards compatibility only. * @deprecated Use one of the Algorithm types instead. */ -export type GeneratorType = CredentialAlgorithm; +export type GeneratorType = PasswordAlgorithm | UsernameAlgorithm | EmailAlgorithm; /** Provided for backwards compatibility only. * @deprecated Use one of the Algorithm types instead. diff --git a/libs/tools/send/send-ui/src/send-form/send-form.module.ts b/libs/tools/send/send-ui/src/send-form/send-form.module.ts index 99db65807ac..df10b563913 100644 --- a/libs/tools/send/send-ui/src/send-form/send-form.module.ts +++ b/libs/tools/send/send-ui/src/send-form/send-form.module.ts @@ -2,8 +2,11 @@ import { NgModule } from "@angular/core"; import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { SafeInjectionToken } from "@bitwarden/angular/services/injection-tokens"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { createRandomizer, @@ -32,7 +35,15 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer"); safeProvider({ useClass: CredentialGeneratorService, provide: CredentialGeneratorService, - deps: [RANDOMIZER, StateProvider, PolicyService], + deps: [ + RANDOMIZER, + StateProvider, + PolicyService, + ApiService, + I18nService, + EncryptService, + CryptoService, + ], }), ], exports: [SendFormComponent], From 74dabb97bfd6902972b5af0d8156845c463d203a Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 23 Oct 2024 19:05:24 +0200 Subject: [PATCH 047/126] Move process reload ownership to key-management (#10853) --- .../browser/src/background/main.background.ts | 19 +++- .../src/background/runtime.background.ts | 6 +- apps/desktop/src/app/app.component.ts | 12 +- .../src/app/services/services.module.ts | 14 ++- .../abstractions/process-reload.service.ts | 6 + .../services/process-reload.service.ts | 106 ++++++++++++++++++ .../platform/abstractions/system.service.ts | 4 - .../src/platform/services/system.service.ts | 99 +--------------- 8 files changed, 147 insertions(+), 119 deletions(-) create mode 100644 libs/common/src/key-management/abstractions/process-reload.service.ts create mode 100644 libs/common/src/key-management/services/process-reload.service.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index e5a4087510c..e31b40fe815 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -75,6 +75,8 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { ProcessReloadService } from "@bitwarden/common/key-management/services/process-reload.service"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -270,6 +272,7 @@ import CommandsBackground from "./commands.background"; import IdleBackground from "./idle.background"; import { NativeMessagingBackground } from "./nativeMessaging.background"; import RuntimeBackground from "./runtime.background"; + export default class MainBackground { messagingService: MessageSender; storageService: BrowserLocalStorageService; @@ -314,6 +317,7 @@ export default class MainBackground { badgeSettingsService: BadgeSettingsServiceAbstraction; domainSettingsService: DomainSettingsService; systemService: SystemServiceAbstraction; + processReloadService: ProcessReloadServiceAbstraction; eventCollectionService: EventCollectionServiceAbstraction; eventUploadService: EventUploadServiceAbstraction; policyService: InternalPolicyServiceAbstraction; @@ -408,7 +412,7 @@ export default class MainBackground { await this.refreshMenu(true); if (this.systemService != null) { await this.systemService.clearPendingClipboard(); - await this.systemService.startProcessReload(this.authService); + await this.processReloadService.startProcessReload(this.authService); } }; @@ -1088,15 +1092,18 @@ export default class MainBackground { }; this.systemService = new SystemService( + this.platformUtilsService, + this.autofillSettingsService, + this.taskSchedulerService, + ); + + this.processReloadService = new ProcessReloadService( this.pinService, this.messagingService, - this.platformUtilsService, systemUtilsServiceReloadCallback, - this.autofillSettingsService, this.vaultTimeoutSettingsService, this.biometricStateService, this.accountService, - this.taskSchedulerService, ); // Other fields @@ -1122,7 +1129,7 @@ export default class MainBackground { this.platformUtilsService as BrowserPlatformUtilsService, this.notificationsService, this.autofillSettingsService, - this.systemService, + this.processReloadService, this.environmentService, this.messagingService, this.logService, @@ -1551,7 +1558,7 @@ export default class MainBackground { await this.mainContextMenuHandler?.noAccess(); await this.notificationsService.updateConnection(false); await this.systemService.clearPendingClipboard(); - await this.systemService.startProcessReload(this.authService); + await this.processReloadService.startProcessReload(this.authService); } private async needsStorageReseed(userId: UserId): Promise { diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 2bc2eadf261..f934c8544bd 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -6,10 +6,10 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { AutofillOverlayVisibility, ExtensionCommand } from "@bitwarden/common/autofill/constants"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { SystemService } from "@bitwarden/common/platform/abstractions/system.service"; import { devFlagEnabled } from "@bitwarden/common/platform/misc/flags"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -40,7 +40,7 @@ export default class RuntimeBackground { private platformUtilsService: BrowserPlatformUtilsService, private notificationsService: NotificationsService, private autofillSettingsService: AutofillSettingsServiceAbstraction, - private systemService: SystemService, + private processReloadSerivce: ProcessReloadServiceAbstraction, private environmentService: BrowserEnvironmentService, private messagingService: MessagingService, private logService: LogService, @@ -216,7 +216,7 @@ export default class RuntimeBackground { } await this.notificationsService.updateConnection(msg.command === "loggedIn"); - this.systemService.cancelProcessReload(); + this.processReloadSerivce.cancelProcessReload(); if (item) { await BrowserApi.focusWindow(item.commandToRetry.sender.tab.windowId); diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index dceda128c85..83dc1619fa1 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -32,6 +32,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -142,6 +143,7 @@ export class AppComponent implements OnInit, OnDestroy { private notificationsService: NotificationsService, private platformUtilsService: PlatformUtilsService, private systemService: SystemService, + private processReloadService: ProcessReloadServiceAbstraction, private stateService: StateService, private eventUploadService: EventUploadService, private policyService: InternalPolicyService, @@ -213,7 +215,7 @@ export class AppComponent implements OnInit, OnDestroy { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.updateAppMenu(); - this.systemService.cancelProcessReload(); + this.processReloadService.cancelProcessReload(); break; case "loggedOut": this.modalService.closeAll(); @@ -224,7 +226,7 @@ export class AppComponent implements OnInit, OnDestroy { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.updateAppMenu(); await this.systemService.clearPendingClipboard(); - await this.systemService.startProcessReload(this.authService); + await this.processReloadService.startProcessReload(this.authService); break; case "authBlocked": // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. @@ -268,15 +270,15 @@ export class AppComponent implements OnInit, OnDestroy { this.notificationsService.updateConnection(); await this.updateAppMenu(); await this.systemService.clearPendingClipboard(); - await this.systemService.startProcessReload(this.authService); + await this.processReloadService.startProcessReload(this.authService); break; case "startProcessReload": // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.systemService.startProcessReload(this.authService); + this.processReloadService.startProcessReload(this.authService); break; case "cancelProcessReload": - this.systemService.cancelProcessReload(); + this.processReloadService.cancelProcessReload(); break; case "reloadProcess": ipc.platform.reloadProcess(); diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index c9b434aa964..36113684425 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -37,6 +37,8 @@ import { import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { ClientType } from "@bitwarden/common/enums"; +import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { ProcessReloadService } from "@bitwarden/common/key-management/services/process-reload.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService, @@ -196,16 +198,22 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: SystemServiceAbstraction, useClass: SystemService, + deps: [ + PlatformUtilsServiceAbstraction, + AutofillSettingsServiceAbstraction, + TaskSchedulerService, + ], + }), + safeProvider({ + provide: ProcessReloadServiceAbstraction, + useClass: ProcessReloadService, deps: [ PinServiceAbstraction, MessagingServiceAbstraction, - PlatformUtilsServiceAbstraction, RELOAD_CALLBACK, - AutofillSettingsServiceAbstraction, VaultTimeoutSettingsService, BiometricStateService, AccountServiceAbstraction, - TaskSchedulerService, ], }), safeProvider({ diff --git a/libs/common/src/key-management/abstractions/process-reload.service.ts b/libs/common/src/key-management/abstractions/process-reload.service.ts new file mode 100644 index 00000000000..e46c1e23199 --- /dev/null +++ b/libs/common/src/key-management/abstractions/process-reload.service.ts @@ -0,0 +1,6 @@ +import { AuthService } from "../../auth/abstractions/auth.service"; + +export abstract class ProcessReloadServiceAbstraction { + abstract startProcessReload(authService: AuthService): Promise; + abstract cancelProcessReload(): void; +} diff --git a/libs/common/src/key-management/services/process-reload.service.ts b/libs/common/src/key-management/services/process-reload.service.ts new file mode 100644 index 00000000000..2f25d63b0fd --- /dev/null +++ b/libs/common/src/key-management/services/process-reload.service.ts @@ -0,0 +1,106 @@ +import { firstValueFrom, map, timeout } from "rxjs"; + +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { BiometricStateService } from "@bitwarden/key-management"; + +import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions"; +import { VaultTimeoutSettingsService } from "../../abstractions/vault-timeout/vault-timeout-settings.service"; +import { AccountService } from "../../auth/abstractions/account.service"; +import { AuthService } from "../../auth/abstractions/auth.service"; +import { AuthenticationStatus } from "../../auth/enums/authentication-status"; +import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; +import { UserId } from "../../types/guid"; +import { ProcessReloadServiceAbstraction } from "../abstractions/process-reload.service"; + +export class ProcessReloadService implements ProcessReloadServiceAbstraction { + private reloadInterval: any = null; + + constructor( + private pinService: PinServiceAbstraction, + private messagingService: MessagingService, + private reloadCallback: () => Promise = null, + private vaultTimeoutSettingsService: VaultTimeoutSettingsService, + private biometricStateService: BiometricStateService, + private accountService: AccountService, + ) {} + + async startProcessReload(authService: AuthService): Promise { + const accounts = await firstValueFrom(this.accountService.accounts$); + if (accounts != null) { + const keys = Object.keys(accounts); + if (keys.length > 0) { + for (const userId of keys) { + let status = await firstValueFrom(authService.authStatusFor$(userId as UserId)); + status = await authService.getAuthStatus(userId); + if (status === AuthenticationStatus.Unlocked) { + return; + } + } + } + } + + // A reloadInterval has already been set and is executing + if (this.reloadInterval != null) { + return; + } + + // If there is an active user, check if they have a pinKeyEncryptedUserKeyEphemeral. If so, prevent process reload upon lock. + const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + if (userId != null) { + const ephemeralPin = await this.pinService.getPinKeyEncryptedUserKeyEphemeral(userId); + if (ephemeralPin != null) { + return; + } + } + + this.cancelProcessReload(); + await this.executeProcessReload(); + } + + private async executeProcessReload() { + const biometricLockedFingerprintValidated = await firstValueFrom( + this.biometricStateService.fingerprintValidated$, + ); + if (!biometricLockedFingerprintValidated) { + clearInterval(this.reloadInterval); + this.reloadInterval = null; + + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe( + map((a) => a?.id), + timeout(500), + ), + ); + // Replace current active user if they will be logged out on reload + if (activeUserId != null) { + const timeoutAction = await firstValueFrom( + this.vaultTimeoutSettingsService + .getVaultTimeoutActionByUserId$(activeUserId) + .pipe(timeout(500)), // safety feature to avoid this call hanging and stopping process reload from clearing memory + ); + if (timeoutAction === VaultTimeoutAction.LogOut) { + const nextUser = await firstValueFrom( + this.accountService.nextUpAccount$.pipe(map((account) => account?.id ?? null)), + ); + await this.accountService.switchAccount(nextUser); + } + } + + this.messagingService.send("reloadProcess"); + if (this.reloadCallback != null) { + await this.reloadCallback(); + } + return; + } + if (this.reloadInterval == null) { + this.reloadInterval = setInterval(async () => await this.executeProcessReload(), 1000); + } + } + + cancelProcessReload(): void { + if (this.reloadInterval != null) { + clearInterval(this.reloadInterval); + this.reloadInterval = null; + } + } +} diff --git a/libs/common/src/platform/abstractions/system.service.ts b/libs/common/src/platform/abstractions/system.service.ts index 204e336fbf4..7a34a313528 100644 --- a/libs/common/src/platform/abstractions/system.service.ts +++ b/libs/common/src/platform/abstractions/system.service.ts @@ -1,8 +1,4 @@ -import { AuthService } from "../../auth/abstractions/auth.service"; - export abstract class SystemService { - abstract startProcessReload(authService: AuthService): Promise; - abstract cancelProcessReload(): void; abstract clearClipboard(clipboardValue: string, timeoutMs?: number): Promise; abstract clearPendingClipboard(): Promise; } diff --git a/libs/common/src/platform/services/system.service.ts b/libs/common/src/platform/services/system.service.ts index 357737391c2..03e96af75b5 100644 --- a/libs/common/src/platform/services/system.service.ts +++ b/libs/common/src/platform/services/system.service.ts @@ -1,16 +1,6 @@ -import { firstValueFrom, map, Subscription, timeout } from "rxjs"; +import { firstValueFrom, Subscription } from "rxjs"; -import { BiometricStateService } from "@bitwarden/key-management"; - -import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions"; -import { VaultTimeoutSettingsService } from "../../abstractions/vault-timeout/vault-timeout-settings.service"; -import { AccountService } from "../../auth/abstractions/account.service"; -import { AuthService } from "../../auth/abstractions/auth.service"; -import { AuthenticationStatus } from "../../auth/enums/authentication-status"; import { AutofillSettingsServiceAbstraction } from "../../autofill/services/autofill-settings.service"; -import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; -import { UserId } from "../../types/guid"; -import { MessagingService } from "../abstractions/messaging.service"; import { PlatformUtilsService } from "../abstractions/platform-utils.service"; import { SystemService as SystemServiceAbstraction } from "../abstractions/system.service"; import { Utils } from "../misc/utils"; @@ -18,19 +8,12 @@ import { ScheduledTaskNames } from "../scheduling/scheduled-task-name.enum"; import { TaskSchedulerService } from "../scheduling/task-scheduler.service"; export class SystemService implements SystemServiceAbstraction { - private reloadInterval: any = null; private clearClipboardTimeoutSubscription: Subscription; private clearClipboardTimeoutFunction: () => Promise = null; constructor( - private pinService: PinServiceAbstraction, - private messagingService: MessagingService, private platformUtilsService: PlatformUtilsService, - private reloadCallback: () => Promise = null, private autofillSettingsService: AutofillSettingsServiceAbstraction, - private vaultTimeoutSettingsService: VaultTimeoutSettingsService, - private biometricStateService: BiometricStateService, - private accountService: AccountService, private taskSchedulerService: TaskSchedulerService, ) { this.taskSchedulerService.registerTaskHandler( @@ -39,86 +22,6 @@ export class SystemService implements SystemServiceAbstraction { ); } - async startProcessReload(authService: AuthService): Promise { - const accounts = await firstValueFrom(this.accountService.accounts$); - if (accounts != null) { - const keys = Object.keys(accounts); - if (keys.length > 0) { - for (const userId of keys) { - let status = await firstValueFrom(authService.authStatusFor$(userId as UserId)); - status = await authService.getAuthStatus(userId); - if (status === AuthenticationStatus.Unlocked) { - return; - } - } - } - } - - // A reloadInterval has already been set and is executing - if (this.reloadInterval != null) { - return; - } - - // If there is an active user, check if they have a pinKeyEncryptedUserKeyEphemeral. If so, prevent process reload upon lock. - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - if (userId != null) { - const ephemeralPin = await this.pinService.getPinKeyEncryptedUserKeyEphemeral(userId); - if (ephemeralPin != null) { - return; - } - } - - this.cancelProcessReload(); - await this.executeProcessReload(); - } - - private async executeProcessReload() { - const biometricLockedFingerprintValidated = await firstValueFrom( - this.biometricStateService.fingerprintValidated$, - ); - if (!biometricLockedFingerprintValidated) { - clearInterval(this.reloadInterval); - this.reloadInterval = null; - - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe( - map((a) => a?.id), - timeout(500), - ), - ); - // Replace current active user if they will be logged out on reload - if (activeUserId != null) { - const timeoutAction = await firstValueFrom( - this.vaultTimeoutSettingsService - .getVaultTimeoutActionByUserId$(activeUserId) - .pipe(timeout(500)), // safety feature to avoid this call hanging and stopping process reload from clearing memory - ); - if (timeoutAction === VaultTimeoutAction.LogOut) { - const nextUser = await firstValueFrom( - this.accountService.nextUpAccount$.pipe(map((account) => account?.id ?? null)), - ); - await this.accountService.switchAccount(nextUser); - } - } - - this.messagingService.send("reloadProcess"); - if (this.reloadCallback != null) { - await this.reloadCallback(); - } - return; - } - if (this.reloadInterval == null) { - this.reloadInterval = setInterval(async () => await this.executeProcessReload(), 1000); - } - } - - cancelProcessReload(): void { - if (this.reloadInterval != null) { - clearInterval(this.reloadInterval); - this.reloadInterval = null; - } - } - async clearClipboard(clipboardValue: string, timeoutMs: number = null): Promise { this.clearClipboardTimeoutSubscription?.unsubscribe(); From 7b8aac229c8aabb31fd77a2ab9a65f9241d6cd2f Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:30:25 -0700 Subject: [PATCH 048/126] [PM-13456] - Password health service (#11658) * add password health service * add spec. fix logic in password reuse * move service to bitwarden_license * revert change to tsconfig * fix spec * fix import --- .../password-health-members.component.ts | 177 +++--------------- .../password-health.component.spec.ts | 78 ++------ .../password-health.component.ts | 172 ++--------------- .../password-health.mock.ts | 66 ------- apps/web/tsconfig.json | 1 + bitwarden_license/bit-common/jest.config.js | 6 +- .../reports/access-intelligence/index.ts | 1 + .../services/ciphers.mock.ts | 128 +++++++++++++ .../access-intelligence/services/index.ts | 2 + .../member-cipher-details-response.mock.ts | 68 +++++++ .../services/password-health.service.spec.ts | 136 ++++++++++++++ .../services/password-health.service.ts | 166 ++++++++++++++++ bitwarden_license/bit-common/test.setup.ts | 1 + 13 files changed, 560 insertions(+), 442 deletions(-) delete mode 100644 apps/web/src/app/tools/access-intelligence/password-health.mock.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/ciphers.mock.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/index.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-response.mock.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.spec.ts create mode 100644 bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.ts create mode 100644 bitwarden_license/bit-common/test.setup.ts diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts b/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts index fd04974b2ce..30c9ad8dba8 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts +++ b/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts @@ -2,17 +2,15 @@ import { CommonModule } from "@angular/common"; import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute } from "@angular/router"; -import { from, map, switchMap, tap } from "rxjs"; +import { map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +// eslint-disable-next-line no-restricted-imports +import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; 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 { BadgeModule, @@ -28,10 +26,6 @@ import { HeaderModule } from "../../layouts/header/header.module"; import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module"; // eslint-disable-next-line no-restricted-imports import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; -// eslint-disable-next-line no-restricted-imports -import { cipherData } from "../reports/pages/reports-ciphers.mock"; - -import { userData } from "./password-health.mock"; @Component({ standalone: true, @@ -47,24 +41,18 @@ import { userData } from "./password-health.mock"; HeaderModule, TableModule, ], + providers: [PasswordHealthService], }) export class PasswordHealthMembersComponent implements OnInit { passwordStrengthMap = new Map(); - weakPasswordCiphers: CipherView[] = []; - passwordUseMap = new Map(); exposedPasswordMap = new Map(); - dataSource = new TableDataSource(); - totalMembersMap = new Map(); - reportCiphers: CipherView[] = []; - reportCipherIds: string[] = []; - - organization: Organization; + dataSource = new TableDataSource(); loading = true; @@ -73,7 +61,6 @@ export class PasswordHealthMembersComponent implements OnInit { constructor( protected cipherService: CipherService, protected passwordStrengthService: PasswordStrengthServiceAbstraction, - protected organizationService: OrganizationService, protected auditService: AuditService, protected i18nService: I18nService, protected activatedRoute: ActivatedRoute, @@ -83,151 +70,29 @@ export class PasswordHealthMembersComponent implements OnInit { this.activatedRoute.paramMap .pipe( takeUntilDestroyed(this.destroyRef), - map((params) => params.get("organizationId")), - switchMap((organizationId) => { - return from(this.organizationService.get(organizationId)); - }), - tap((organization) => { - this.organization = organization; + map(async (params) => { + const organizationId = params.get("organizationId"); + await this.setCiphers(organizationId); }), - switchMap(() => from(this.setCiphers())), ) .subscribe(); - - // mock data - will be replaced with actual data - userData.forEach((user) => { - user.cipherIds.forEach((cipherId: string) => { - if (this.totalMembersMap.has(cipherId)) { - this.totalMembersMap.set(cipherId, (this.totalMembersMap.get(cipherId) || 0) + 1); - } else { - this.totalMembersMap.set(cipherId, 1); - } - }); - }); } - async setCiphers() { - // const allCiphers = await this.cipherService.getAllFromApiForOrganization(this.organization.id); - const allCiphers = cipherData; - allCiphers.forEach(async (cipher) => { - this.findWeakPassword(cipher); - this.findReusedPassword(cipher); - await this.findExposedPassword(cipher); - }); - this.dataSource.data = this.reportCiphers; - this.loading = false; - } - - protected checkForExistingCipher(ciph: CipherView) { - if (!this.reportCipherIds.includes(ciph.id)) { - this.reportCipherIds.push(ciph.id); - this.reportCiphers.push(ciph); - } - } - - protected async findExposedPassword(cipher: CipherView) { - const { type, login, isDeleted, edit, viewPassword, id } = cipher; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } - - const exposedCount = await this.auditService.passwordLeaked(login.password); - if (exposedCount > 0) { - this.exposedPasswordMap.set(id, exposedCount); - this.checkForExistingCipher(cipher); - } - } - - protected findReusedPassword(cipher: CipherView) { - const { type, login, isDeleted, edit, viewPassword } = cipher; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } - - if (this.passwordUseMap.has(login.password)) { - this.passwordUseMap.set(login.password, this.passwordUseMap.get(login.password) || 0 + 1); - } else { - this.passwordUseMap.set(login.password, 1); - } - - this.checkForExistingCipher(cipher); - } - - protected findWeakPassword(cipher: CipherView): void { - const { type, login, isDeleted, edit, viewPassword } = cipher; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } - - const hasUserName = this.isUserNameNotEmpty(cipher); - let userInput: string[] = []; - if (hasUserName) { - const atPosition = login.username.indexOf("@"); - if (atPosition > -1) { - userInput = userInput - .concat( - login.username - .substring(0, atPosition) - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/), - ) - .filter((i) => i.length >= 3); - } else { - userInput = login.username - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/) - .filter((i) => i.length >= 3); - } - } - const { score } = this.passwordStrengthService.getPasswordStrength( - login.password, - null, - userInput.length > 0 ? userInput : null, + async setCiphers(organizationId: string) { + const passwordHealthService = new PasswordHealthService( + this.passwordStrengthService, + this.auditService, + this.cipherService, + organizationId, ); - if (score != null && score <= 2) { - this.passwordStrengthMap.set(cipher.id, this.scoreKey(score)); - this.checkForExistingCipher(cipher); - } - } - - private isUserNameNotEmpty(c: CipherView): boolean { - return !Utils.isNullOrWhitespace(c.login.username); - } + await passwordHealthService.generateReport(); - private scoreKey(score: number): [string, BadgeVariant] { - switch (score) { - case 4: - return ["strong", "success"]; - case 3: - return ["good", "primary"]; - case 2: - return ["weak", "warning"]; - default: - return ["veryWeak", "danger"]; - } + this.dataSource.data = passwordHealthService.reportCiphers; + this.exposedPasswordMap = passwordHealthService.exposedPasswordMap; + this.passwordStrengthMap = passwordHealthService.passwordStrengthMap; + this.passwordUseMap = passwordHealthService.passwordUseMap; + this.totalMembersMap = passwordHealthService.totalMembersMap; + this.loading = false; } } diff --git a/apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts b/apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts index 4a6d5c50ee1..d41807e7d2d 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts +++ b/apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts @@ -1,11 +1,11 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ActivatedRoute, convertToParamMap } from "@angular/router"; -import { MockProxy, mock } from "jest-mock-extended"; +import { mock } from "jest-mock-extended"; import { of } from "rxjs"; +// eslint-disable-next-line no-restricted-imports +import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -14,39 +14,30 @@ import { TableBodyDirective } from "@bitwarden/components/src/table/table.compon import { LooseComponentsModule } from "../../shared"; import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; -// eslint-disable-next-line no-restricted-imports -import { cipherData } from "../reports/pages/reports-ciphers.mock"; import { PasswordHealthComponent } from "./password-health.component"; describe("PasswordHealthComponent", () => { let component: PasswordHealthComponent; let fixture: ComponentFixture; - let passwordStrengthService: MockProxy; - let organizationService: MockProxy; - let cipherServiceMock: MockProxy; - let auditServiceMock: MockProxy; const activeRouteParams = convertToParamMap({ organizationId: "orgId" }); beforeEach(async () => { - passwordStrengthService = mock(); - auditServiceMock = mock(); - organizationService = mock({ - get: jest.fn().mockResolvedValue({ id: "orgId" } as Organization), - }); - cipherServiceMock = mock({ - getAllFromApiForOrganization: jest.fn().mockResolvedValue(cipherData), - }); - await TestBed.configureTestingModule({ imports: [PasswordHealthComponent, PipesModule, TableModule, LooseComponentsModule], declarations: [TableBodyDirective], providers: [ - { provide: CipherService, useValue: cipherServiceMock }, - { provide: PasswordStrengthServiceAbstraction, useValue: passwordStrengthService }, - { provide: OrganizationService, useValue: organizationService }, + { provide: CipherService, useValue: mock() }, { provide: I18nService, useValue: mock() }, - { provide: AuditService, useValue: auditServiceMock }, + { provide: AuditService, useValue: mock() }, + { + provide: PasswordStrengthServiceAbstraction, + useValue: mock(), + }, + { + provide: PasswordHealthService, + useValue: mock(), + }, { provide: ActivatedRoute, useValue: { @@ -69,46 +60,5 @@ describe("PasswordHealthComponent", () => { expect(component).toBeTruthy(); }); - it("should populate reportCiphers with ciphers that have password issues", async () => { - passwordStrengthService.getPasswordStrength.mockReturnValue({ score: 1 } as any); - - auditServiceMock.passwordLeaked.mockResolvedValue(5); - - await component.setCiphers(); - - const cipherIds = component.reportCiphers.map((c) => c.id); - - expect(cipherIds).toEqual([ - "cbea34a8-bde4-46ad-9d19-b05001228ab1", - "cbea34a8-bde4-46ad-9d19-b05001228ab2", - "cbea34a8-bde4-46ad-9d19-b05001228cd3", - ]); - expect(component.reportCiphers.length).toEqual(3); - }); - - it("should correctly populate passwordStrengthMap", async () => { - passwordStrengthService.getPasswordStrength.mockImplementation((password) => { - let score = 0; - if (password === "123") { - score = 1; - } else { - score = 4; - } - return { score } as any; - }); - - auditServiceMock.passwordLeaked.mockResolvedValue(0); - - await component.setCiphers(); - - expect(component.passwordStrengthMap.size).toBeGreaterThan(0); - expect(component.passwordStrengthMap.get("cbea34a8-bde4-46ad-9d19-b05001228ab2")).toEqual([ - "veryWeak", - "danger", - ]); - expect(component.passwordStrengthMap.get("cbea34a8-bde4-46ad-9d19-b05001228cd3")).toEqual([ - "veryWeak", - "danger", - ]); - }); + it("should call generateReport on init", () => {}); }); diff --git a/apps/web/src/app/tools/access-intelligence/password-health.component.ts b/apps/web/src/app/tools/access-intelligence/password-health.component.ts index 6e8e62c50db..4b7b8e394d3 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health.component.ts +++ b/apps/web/src/app/tools/access-intelligence/password-health.component.ts @@ -2,17 +2,15 @@ import { CommonModule } from "@angular/common"; import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute } from "@angular/router"; -import { from, map, switchMap, tap } from "rxjs"; +import { map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +// eslint-disable-next-line no-restricted-imports +import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; 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 { BadgeModule, @@ -43,23 +41,17 @@ import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; HeaderModule, TableModule, ], + providers: [PasswordHealthService], }) export class PasswordHealthComponent implements OnInit { passwordStrengthMap = new Map(); - weakPasswordCiphers: CipherView[] = []; - passwordUseMap = new Map(); exposedPasswordMap = new Map(); dataSource = new TableDataSource(); - reportCiphers: CipherView[] = []; - reportCipherIds: string[] = []; - - organization: Organization; - loading = true; private destroyRef = inject(DestroyRef); @@ -67,7 +59,6 @@ export class PasswordHealthComponent implements OnInit { constructor( protected cipherService: CipherService, protected passwordStrengthService: PasswordStrengthServiceAbstraction, - protected organizationService: OrganizationService, protected auditService: AuditService, protected i18nService: I18nService, protected activatedRoute: ActivatedRoute, @@ -77,153 +68,28 @@ export class PasswordHealthComponent implements OnInit { this.activatedRoute.paramMap .pipe( takeUntilDestroyed(this.destroyRef), - map((params) => params.get("organizationId")), - switchMap((organizationId) => { - return from(this.organizationService.get(organizationId)); + map(async (params) => { + const organizationId = params.get("organizationId"); + await this.setCiphers(organizationId); }), - tap((organization) => { - this.organization = organization; - }), - switchMap(() => from(this.setCiphers())), ) .subscribe(); } - async setCiphers() { - const allCiphers = await this.cipherService.getAllFromApiForOrganization(this.organization.id); - allCiphers.forEach(async (cipher) => { - this.findWeakPassword(cipher); - this.findReusedPassword(cipher); - await this.findExposedPassword(cipher); - }); - this.dataSource.data = this.reportCiphers; - this.loading = false; - - // const reportIssues = allCiphers.map((c) => { - // if (this.passwordStrengthMap.has(c.id)) { - // return c; - // } - - // if (this.passwordUseMap.has(c.id)) { - // return c; - // } - - // if (this.exposedPasswordMap.has(c.id)) { - // return c; - // } - // }); - } - - protected checkForExistingCipher(ciph: CipherView) { - if (!this.reportCipherIds.includes(ciph.id)) { - this.reportCipherIds.push(ciph.id); - this.reportCiphers.push(ciph); - } - } - - protected async findExposedPassword(cipher: CipherView) { - const { type, login, isDeleted, edit, viewPassword, id } = cipher; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } - - const exposedCount = await this.auditService.passwordLeaked(login.password); - if (exposedCount > 0) { - this.exposedPasswordMap.set(id, exposedCount); - this.checkForExistingCipher(cipher); - } - } - - protected findReusedPassword(cipher: CipherView) { - const { type, login, isDeleted, edit, viewPassword } = cipher; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } - - if (this.passwordUseMap.has(login.password)) { - this.passwordUseMap.set(login.password, this.passwordUseMap.get(login.password) || 0 + 1); - } else { - this.passwordUseMap.set(login.password, 1); - } - - this.checkForExistingCipher(cipher); - } - - protected findWeakPassword(cipher: CipherView): void { - const { type, login, isDeleted, edit, viewPassword } = cipher; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { - return; - } - - const hasUserName = this.isUserNameNotEmpty(cipher); - let userInput: string[] = []; - if (hasUserName) { - const atPosition = login.username.indexOf("@"); - if (atPosition > -1) { - userInput = userInput - .concat( - login.username - .substring(0, atPosition) - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/), - ) - .filter((i) => i.length >= 3); - } else { - userInput = login.username - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/) - .filter((i) => i.length >= 3); - } - } - const { score } = this.passwordStrengthService.getPasswordStrength( - login.password, - null, - userInput.length > 0 ? userInput : null, + async setCiphers(organizationId: string) { + const passwordHealthService = new PasswordHealthService( + this.passwordStrengthService, + this.auditService, + this.cipherService, + organizationId, ); - if (score != null && score <= 2) { - this.passwordStrengthMap.set(cipher.id, this.scoreKey(score)); - this.checkForExistingCipher(cipher); - } - } - - private isUserNameNotEmpty(c: CipherView): boolean { - return !Utils.isNullOrWhitespace(c.login.username); - } + await passwordHealthService.generateReport(); - private scoreKey(score: number): [string, BadgeVariant] { - switch (score) { - case 4: - return ["strong", "success"]; - case 3: - return ["good", "primary"]; - case 2: - return ["weak", "warning"]; - default: - return ["veryWeak", "danger"]; - } + this.dataSource.data = passwordHealthService.reportCiphers; + this.exposedPasswordMap = passwordHealthService.exposedPasswordMap; + this.passwordStrengthMap = passwordHealthService.passwordStrengthMap; + this.passwordUseMap = passwordHealthService.passwordUseMap; + this.loading = false; } } diff --git a/apps/web/src/app/tools/access-intelligence/password-health.mock.ts b/apps/web/src/app/tools/access-intelligence/password-health.mock.ts deleted file mode 100644 index d01edc37a59..00000000000 --- a/apps/web/src/app/tools/access-intelligence/password-health.mock.ts +++ /dev/null @@ -1,66 +0,0 @@ -export const userData: any[] = [ - { - userName: "David Brent", - email: "david.brent@wernhamhogg.uk", - usesKeyConnector: true, - cipherIds: [ - "cbea34a8-bde4-46ad-9d19-b05001228ab1", - "cbea34a8-bde4-46ad-9d19-b05001228ab2", - "cbea34a8-bde4-46ad-9d19-b05001228xy4", - "cbea34a8-bde4-46ad-9d19-b05001227nm5", - ], - }, - { - userName: "Tim Canterbury", - email: "tim.canterbury@wernhamhogg.uk", - usesKeyConnector: false, - cipherIds: [ - "cbea34a8-bde4-46ad-9d19-b05001228ab2", - "cbea34a8-bde4-46ad-9d19-b05001228cd3", - "cbea34a8-bde4-46ad-9d19-b05001228xy4", - "cbea34a8-bde4-46ad-9d19-b05001227nm5", - ], - }, - { - userName: "Gareth Keenan", - email: "gareth.keenan@wernhamhogg.uk", - usesKeyConnector: true, - cipherIds: [ - "cbea34a8-bde4-46ad-9d19-b05001228cd3", - "cbea34a8-bde4-46ad-9d19-b05001228xy4", - "cbea34a8-bde4-46ad-9d19-b05001227nm5", - "cbea34a8-bde4-46ad-9d19-b05001227nm7", - ], - }, - { - userName: "Dawn Tinsley", - email: "dawn.tinsley@wernhamhogg.uk", - usesKeyConnector: true, - cipherIds: [ - "cbea34a8-bde4-46ad-9d19-b05001228ab2", - "cbea34a8-bde4-46ad-9d19-b05001228cd3", - "cbea34a8-bde4-46ad-9d19-b05001228xy4", - ], - }, - { - userName: "Keith Bishop", - email: "keith.bishop@wernhamhogg.uk", - usesKeyConnector: false, - cipherIds: [ - "cbea34a8-bde4-46ad-9d19-b05001228ab1", - "cbea34a8-bde4-46ad-9d19-b05001228cd3", - "cbea34a8-bde4-46ad-9d19-b05001228xy4", - "cbea34a8-bde4-46ad-9d19-b05001227nm5", - ], - }, - { - userName: "Chris Finch", - email: "chris.finch@wernhamhogg.uk", - usesKeyConnector: true, - cipherIds: [ - "cbea34a8-bde4-46ad-9d19-b05001228ab2", - "cbea34a8-bde4-46ad-9d19-b05001228cd3", - "cbea34a8-bde4-46ad-9d19-b05001228xy4", - ], - }, -]; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 3b0c897e91e..3799945ea98 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -10,6 +10,7 @@ "@bitwarden/auth/common": ["../../libs/auth/src/common"], "@bitwarden/auth/angular": ["../../libs/auth/src/angular"], "@bitwarden/billing": ["../../libs/billing/src"], + "@bitwarden/bit-common/*": ["../../bitwarden_license/bit-common/src/*"], "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/components": ["../../libs/components/src"], "@bitwarden/generator-components": ["../../libs/tools/generator/components/src"], diff --git a/bitwarden_license/bit-common/jest.config.js b/bitwarden_license/bit-common/jest.config.js index d79f8ae6199..a0441b01883 100644 --- a/bitwarden_license/bit-common/jest.config.js +++ b/bitwarden_license/bit-common/jest.config.js @@ -1,16 +1,16 @@ const { pathsToModuleNameMapper } = require("ts-jest"); - const { compilerOptions } = require("./tsconfig"); - const sharedConfig = require("../../libs/shared/jest.config.angular"); /** @type {import('jest').Config} */ module.exports = { ...sharedConfig, displayName: "bit-common tests", - preset: "ts-jest", testEnvironment: "jsdom", moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { prefix: "/", }), + setupFilesAfterEnv: ["/test.setup.ts"], + transformIgnorePatterns: ["node_modules/(?!(.*\\.mjs$|@angular|rxjs|@bitwarden))"], + moduleFileExtensions: ["ts", "js", "html", "mjs"], }; diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts index e69de29bb2d..b2221a94a89 100644 --- a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/index.ts @@ -0,0 +1 @@ +export * from "./services"; diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/ciphers.mock.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/ciphers.mock.ts new file mode 100644 index 00000000000..22b9148c840 --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/ciphers.mock.ts @@ -0,0 +1,128 @@ +export const mockCiphers: any[] = [ + { + initializerKey: 1, + id: "cbea34a8-bde4-46ad-9d19-b05001228ab1", + organizationId: null, + folderId: null, + name: "Cannot Be Edited", + notes: null, + isDeleted: false, + type: 1, + favorite: false, + organizationUseTotp: false, + login: { + password: "123", + }, + edit: false, + viewPassword: true, + collectionIds: [], + revisionDate: "2023-08-03T17:40:59.793Z", + creationDate: "2023-08-03T17:40:59.793Z", + deletedDate: null, + reprompt: 0, + localData: null, + }, + { + initializerKey: 1, + id: "cbea34a8-bde4-46ad-9d19-b05001228ab2", + organizationId: null, + folderId: null, + name: "Can Be Edited id ending 2", + notes: null, + isDeleted: false, + type: 1, + favorite: false, + organizationUseTotp: false, + login: { + password: "123", + hasUris: true, + uris: [ + { + uri: "http://nothing.com", + }, + ], + }, + edit: true, + viewPassword: true, + collectionIds: [], + revisionDate: "2023-08-03T17:40:59.793Z", + creationDate: "2023-08-03T17:40:59.793Z", + deletedDate: null, + reprompt: 0, + localData: null, + }, + { + initializerKey: 1, + id: "cbea34a8-bde4-46ad-9d19-b05001228cd3", + organizationId: null, + folderId: null, + name: "Can Be Edited id ending 3", + notes: null, + type: 1, + favorite: false, + organizationUseTotp: false, + login: { + password: "123", + hasUris: true, + uris: [ + { + uri: "http://example.com", + }, + ], + }, + edit: true, + viewPassword: true, + collectionIds: [], + revisionDate: "2023-08-03T17:40:59.793Z", + creationDate: "2023-08-03T17:40:59.793Z", + deletedDate: null, + reprompt: 0, + localData: null, + }, + { + initializerKey: 1, + id: "cbea34a8-bde4-46ad-9d19-b05001228xy4", + organizationId: null, + folderId: null, + name: "Can Be Edited id ending 4", + notes: null, + type: 1, + favorite: false, + organizationUseTotp: false, + login: { + hasUris: true, + uris: [{ uri: "101domain.com" }], + }, + edit: true, + viewPassword: true, + collectionIds: [], + revisionDate: "2023-08-03T17:40:59.793Z", + creationDate: "2023-08-03T17:40:59.793Z", + deletedDate: null, + reprompt: 0, + localData: null, + }, + { + initializerKey: 1, + id: "cbea34a8-bde4-46ad-9d19-b05001227nm5", + organizationId: null, + folderId: null, + name: "Can Be Edited id ending 5", + notes: null, + type: 1, + favorite: false, + organizationUseTotp: false, + login: { + hasUris: true, + uris: [{ uri: "123formbuilder.com" }], + }, + edit: true, + viewPassword: true, + collectionIds: [], + revisionDate: "2023-08-03T17:40:59.793Z", + creationDate: "2023-08-03T17:40:59.793Z", + deletedDate: null, + reprompt: 0, + localData: null, + }, +]; diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/index.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/index.ts new file mode 100644 index 00000000000..c7bace84e5b --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/index.ts @@ -0,0 +1,2 @@ +export * from "./member-cipher-details-api.service"; +export * from "./password-health.service"; diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-response.mock.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-response.mock.ts new file mode 100644 index 00000000000..78cc105e9b4 --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/member-cipher-details-response.mock.ts @@ -0,0 +1,68 @@ +export const mockMemberCipherDetailsResponse: { data: any[] } = { + data: [ + { + UserName: "David Brent", + Email: "david.brent@wernhamhogg.uk", + UsesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab1", + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + ], + }, + { + UserName: "Tim Canterbury", + Email: "tim.canterbury@wernhamhogg.uk", + UsesKeyConnector: false, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + ], + }, + { + UserName: "Gareth Keenan", + Email: "gareth.keenan@wernhamhogg.uk", + UsesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + "cbea34a8-bde4-46ad-9d19-b05001227nm7", + ], + }, + { + UserName: "Dawn Tinsley", + Email: "dawn.tinsley@wernhamhogg.uk", + UsesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + ], + }, + { + UserName: "Keith Bishop", + Email: "keith.bishop@wernhamhogg.uk", + UsesKeyConnector: false, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab1", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + "cbea34a8-bde4-46ad-9d19-b05001227nm5", + ], + }, + { + UserName: "Chris Finch", + Email: "chris.finch@wernhamhogg.uk", + UsesKeyConnector: true, + cipherIds: [ + "cbea34a8-bde4-46ad-9d19-b05001228ab2", + "cbea34a8-bde4-46ad-9d19-b05001228cd3", + "cbea34a8-bde4-46ad-9d19-b05001228xy4", + ], + }, + ], +}; diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.spec.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.spec.ts new file mode 100644 index 00000000000..8f391b7d569 --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.spec.ts @@ -0,0 +1,136 @@ +import { TestBed } from "@angular/core/testing"; + +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; + +import { mockCiphers } from "./ciphers.mock"; +import { PasswordHealthService } from "./password-health.service"; + +describe("PasswordHealthService", () => { + let service: PasswordHealthService; + let cipherService: CipherService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + PasswordHealthService, + { + provide: PasswordStrengthServiceAbstraction, + useValue: { + getPasswordStrength: (password: string) => { + const score = password.length < 4 ? 1 : 4; + return { score }; + }, + }, + }, + { + provide: AuditService, + useValue: { + passwordLeaked: (password: string) => Promise.resolve(password === "123" ? 100 : 0), + }, + }, + { + provide: CipherService, + useValue: { + getAllFromApiForOrganization: jest.fn().mockResolvedValue(CipherData), + }, + }, + { provide: "organizationId", useValue: "org1" }, + ], + }); + + service = TestBed.inject(PasswordHealthService); + cipherService = TestBed.inject(CipherService); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should initialize properties", () => { + expect(service.reportCiphers).toEqual([]); + expect(service.reportCipherIds).toEqual([]); + expect(service.passwordStrengthMap.size).toBe(0); + expect(service.passwordUseMap.size).toBe(0); + expect(service.exposedPasswordMap.size).toBe(0); + expect(service.totalMembersMap.size).toBe(0); + }); + + describe("generateReport", () => { + beforeEach(async () => { + await service.generateReport(); + }); + + it("should fetch all ciphers for the organization", () => { + expect(cipherService.getAllFromApiForOrganization).toHaveBeenCalledWith("org1"); + }); + + it("should populate reportCiphers with ciphers that have issues", () => { + expect(service.reportCiphers.length).toBeGreaterThan(0); + }); + + it("should detect weak passwords", () => { + expect(service.passwordStrengthMap.size).toBeGreaterThan(0); + expect(service.passwordStrengthMap.get("cbea34a8-bde4-46ad-9d19-b05001228ab1")).toEqual([ + "veryWeak", + "danger", + ]); + expect(service.passwordStrengthMap.get("cbea34a8-bde4-46ad-9d19-b05001228ab2")).toEqual([ + "veryWeak", + "danger", + ]); + expect(service.passwordStrengthMap.get("cbea34a8-bde4-46ad-9d19-b05001228cd3")).toEqual([ + "veryWeak", + "danger", + ]); + }); + + it("should detect reused passwords", () => { + expect(service.passwordUseMap.get("123")).toBe(3); + }); + + it("should detect exposed passwords", () => { + expect(service.exposedPasswordMap.size).toBeGreaterThan(0); + expect(service.exposedPasswordMap.get("cbea34a8-bde4-46ad-9d19-b05001228ab1")).toBe(100); + }); + + it("should calculate total members per cipher", () => { + expect(service.totalMembersMap.size).toBeGreaterThan(0); + expect(service.totalMembersMap.get("cbea34a8-bde4-46ad-9d19-b05001228ab1")).toBe(2); + expect(service.totalMembersMap.get("cbea34a8-bde4-46ad-9d19-b05001228ab2")).toBe(4); + expect(service.totalMembersMap.get("cbea34a8-bde4-46ad-9d19-b05001228cd3")).toBe(5); + expect(service.totalMembersMap.get("cbea34a8-bde4-46ad-9d19-b05001227nm5")).toBe(4); + expect(service.totalMembersMap.get("cbea34a8-bde4-46ad-9d19-b05001227nm7")).toBe(1); + expect(service.totalMembersMap.get("cbea34a8-bde4-46ad-9d19-b05001228xy4")).toBe(6); + }); + }); + + describe("findWeakPassword", () => { + it("should add weak passwords to passwordStrengthMap", () => { + const weakCipher = mockCiphers.find((c) => c.login?.password === "123") as CipherView; + service.findWeakPassword(weakCipher); + expect(service.passwordStrengthMap.get(weakCipher.id)).toEqual(["veryWeak", "danger"]); + }); + }); + + describe("findReusedPassword", () => { + it("should detect password reuse", () => { + mockCiphers.forEach((cipher) => { + service.findReusedPassword(cipher as CipherView); + }); + const reuseCounts = Array.from(service.passwordUseMap.values()).filter((count) => count > 1); + expect(reuseCounts.length).toBeGreaterThan(0); + }); + }); + + describe("findExposedPassword", () => { + it("should add exposed passwords to exposedPasswordMap", async () => { + const exposedCipher = mockCiphers.find((c) => c.login?.password === "123") as CipherView; + await service.findExposedPassword(exposedCipher); + expect(service.exposedPasswordMap.get(exposedCipher.id)).toBe(100); + }); + }); +}); diff --git a/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.ts b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.ts new file mode 100644 index 00000000000..ce78aba426d --- /dev/null +++ b/bitwarden_license/bit-common/src/tools/reports/access-intelligence/services/password-health.service.ts @@ -0,0 +1,166 @@ +import { Inject, Injectable } from "@angular/core"; + +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; +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 { BadgeVariant } from "@bitwarden/components"; + +import { mockCiphers } from "./ciphers.mock"; +import { mockMemberCipherDetailsResponse } from "./member-cipher-details-response.mock"; + +@Injectable() +export class PasswordHealthService { + reportCiphers: CipherView[] = []; + + reportCipherIds: string[] = []; + + passwordStrengthMap = new Map(); + + passwordUseMap = new Map(); + + exposedPasswordMap = new Map(); + + totalMembersMap = new Map(); + + constructor( + private passwordStrengthService: PasswordStrengthServiceAbstraction, + private auditService: AuditService, + private cipherService: CipherService, + @Inject("organizationId") private organizationId: string, + ) {} + + async generateReport() { + let allCiphers = await this.cipherService.getAllFromApiForOrganization(this.organizationId); + // TODO remove when actual user member data is available + allCiphers = mockCiphers; + allCiphers.forEach(async (cipher) => { + this.findWeakPassword(cipher); + this.findReusedPassword(cipher); + await this.findExposedPassword(cipher); + }); + + // TODO - fetch actual user member when data is available + mockMemberCipherDetailsResponse.data.forEach((user) => { + user.cipherIds.forEach((cipherId: string) => { + if (this.totalMembersMap.has(cipherId)) { + this.totalMembersMap.set(cipherId, (this.totalMembersMap.get(cipherId) || 0) + 1); + } else { + this.totalMembersMap.set(cipherId, 1); + } + }); + }); + } + + async findExposedPassword(cipher: CipherView) { + const { type, login, isDeleted, viewPassword, id } = cipher; + if ( + type !== CipherType.Login || + login.password == null || + login.password === "" || + isDeleted || + !viewPassword + ) { + return; + } + + const exposedCount = await this.auditService.passwordLeaked(login.password); + if (exposedCount > 0) { + this.exposedPasswordMap.set(id, exposedCount); + this.checkForExistingCipher(cipher); + } + } + + findReusedPassword(cipher: CipherView) { + const { type, login, isDeleted, viewPassword } = cipher; + if ( + type !== CipherType.Login || + login.password == null || + login.password === "" || + isDeleted || + !viewPassword + ) { + return; + } + + if (this.passwordUseMap.has(login.password)) { + this.passwordUseMap.set(login.password, (this.passwordUseMap.get(login.password) || 0) + 1); + } else { + this.passwordUseMap.set(login.password, 1); + } + + this.checkForExistingCipher(cipher); + } + + findWeakPassword(cipher: CipherView): void { + const { type, login, isDeleted, viewPassword } = cipher; + if ( + type !== CipherType.Login || + login.password == null || + login.password === "" || + isDeleted || + !viewPassword + ) { + return; + } + + const hasUserName = this.isUserNameNotEmpty(cipher); + let userInput: string[] = []; + if (hasUserName) { + const atPosition = login.username.indexOf("@"); + if (atPosition > -1) { + userInput = userInput + .concat( + login.username + .substring(0, atPosition) + .trim() + .toLowerCase() + .split(/[^A-Za-z0-9]/), + ) + .filter((i) => i.length >= 3); + } else { + userInput = login.username + .trim() + .toLowerCase() + .split(/[^A-Za-z0-9]/) + .filter((i) => i.length >= 3); + } + } + const { score } = this.passwordStrengthService.getPasswordStrength( + login.password, + null, + userInput.length > 0 ? userInput : null, + ); + + if (score != null && score <= 2) { + this.passwordStrengthMap.set(cipher.id, this.scoreKey(score)); + this.checkForExistingCipher(cipher); + } + } + + private isUserNameNotEmpty(c: CipherView): boolean { + return !Utils.isNullOrWhitespace(c.login.username); + } + + private scoreKey(score: number): [string, BadgeVariant] { + switch (score) { + case 4: + return ["strong", "success"]; + case 3: + return ["good", "primary"]; + case 2: + return ["weak", "warning"]; + default: + return ["veryWeak", "danger"]; + } + } + + private checkForExistingCipher(ciph: CipherView) { + if (!this.reportCipherIds.includes(ciph.id)) { + this.reportCipherIds.push(ciph.id); + this.reportCiphers.push(ciph); + } + } +} diff --git a/bitwarden_license/bit-common/test.setup.ts b/bitwarden_license/bit-common/test.setup.ts new file mode 100644 index 00000000000..a702c633967 --- /dev/null +++ b/bitwarden_license/bit-common/test.setup.ts @@ -0,0 +1 @@ +import "jest-preset-angular/setup-jest"; From a8299e7040c4b1e69c941843e84e046e2bcdc705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=A8=20Audrey=20=E2=9C=A8?= Date: Wed, 23 Oct 2024 14:17:24 -0400 Subject: [PATCH 049/126] fix generate a11y binding (#11671) --- .../generator/components/src/credential-generator.component.ts | 2 +- .../generator/components/src/password-generator.component.ts | 2 +- .../generator/components/src/username-generator.component.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts index 25aff97f16c..c57aaadeece 100644 --- a/libs/tools/generator/components/src/credential-generator.component.ts +++ b/libs/tools/generator/components/src/credential-generator.component.ts @@ -456,7 +456,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { */ protected credentialTypeGenerateLabel$ = this.algorithm$.pipe( filter((algorithm) => !!algorithm), - map(({ copy }) => copy), + map(({ generate }) => generate), ); /** Emits hint key for the currently selected credential type */ diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts index f6ec1b17e2d..ff2cc21d541 100644 --- a/libs/tools/generator/components/src/password-generator.component.ts +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -193,7 +193,7 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy { */ protected credentialTypeGenerateLabel$ = this.algorithm$.pipe( filter((algorithm) => !!algorithm), - map(({ copy }) => copy), + map(({ generate }) => generate), ); private toOptions(algorithms: AlgorithmInfo[]) { diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts index 083ef32a3b0..ea75ef6079c 100644 --- a/libs/tools/generator/components/src/username-generator.component.ts +++ b/libs/tools/generator/components/src/username-generator.component.ts @@ -378,7 +378,7 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { */ protected credentialTypeGenerateLabel$ = this.algorithm$.pipe( filter((algorithm) => !!algorithm), - map(({ copy }) => copy), + map(({ generate }) => generate), ); /** Emits hint key for the currently selected credential type */ From 7c79487f041292a13ca2867ba67b048fe10a63d0 Mon Sep 17 00:00:00 2001 From: SamFrank234 <95505948+SamFrank234@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:21:56 -0400 Subject: [PATCH 050/126] [PM-7565] fix filter icon alignment (#8790) update styles so that folders and subfolders are correctly aligned in vault filters on web and desktop --- apps/desktop/src/scss/left-nav.scss | 2 ++ apps/web/src/scss/vault-filters.scss | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/desktop/src/scss/left-nav.scss b/apps/desktop/src/scss/left-nav.scss index dc882ad265d..4404110ba65 100644 --- a/apps/desktop/src/scss/left-nav.scss +++ b/apps/desktop/src/scss/left-nav.scss @@ -141,6 +141,8 @@ color: themed("headingButtonColor"); } + margin-right: 0.25rem; + &:hover, &:focus { @include themify($themes) { diff --git a/apps/web/src/scss/vault-filters.scss b/apps/web/src/scss/vault-filters.scss index 01c3903c507..27b4b8164f9 100644 --- a/apps/web/src/scss/vault-filters.scss +++ b/apps/web/src/scss/vault-filters.scss @@ -43,6 +43,7 @@ button.toggle-button, button.add-button { + margin-right: 0.25rem; &:hover, &:focus { @include themify($themes) { @@ -98,6 +99,7 @@ } .toggle-button { + margin-right: 0.25rem; &:hover, &:focus { @include themify($themes) { From 22be52d2f3912d911e899ad8c53ac8534047633c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=A8=20Audrey=20=E2=9C=A8?= Date: Wed, 23 Oct 2024 14:23:28 -0400 Subject: [PATCH 051/126] [PM-12303] fix password state spurious emissions (#11670) * trace generation requests * eliminate spurious save caused by validator changes * fix emissions caused by setting bounds attrbutes --------- Co-authored-by: Daniel James Smith --- .../src/credential-generator.component.html | 16 +++++----- .../src/credential-generator.component.ts | 12 +++++-- .../src/forwarder-settings.component.ts | 2 ++ .../src/passphrase-settings.component.html | 9 +----- .../src/passphrase-settings.component.ts | 11 ++----- .../src/password-generator.component.html | 10 +++--- .../src/password-generator.component.ts | 12 +++++-- .../src/password-settings.component.html | 24 ++------------ .../src/password-settings.component.ts | 31 +------------------ .../src/username-generator.component.html | 16 ++++++---- .../src/username-generator.component.ts | 12 +++++-- 11 files changed, 64 insertions(+), 91 deletions(-) diff --git a/libs/tools/generator/components/src/credential-generator.component.html b/libs/tools/generator/components/src/credential-generator.component.html index 4c9fb9e7e49..06ea1f767b7 100644 --- a/libs/tools/generator/components/src/credential-generator.component.html +++ b/libs/tools/generator/components/src/credential-generator.component.html @@ -20,9 +20,11 @@ type="button" bitIconButton="bwi-generate" buttonType="main" - (click)="generate$.next()" + (click)="generate('user request')" [appA11yTitle]="credentialTypeGenerateLabel$ | async" - > + > + {{ credentialTypeGenerateLabel$ | async }} +
diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts index c57aaadeece..a37de986499 100644 --- a/libs/tools/generator/components/src/credential-generator.component.ts +++ b/libs/tools/generator/components/src/credential-generator.component.ts @@ -376,7 +376,7 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { if (!a || a.onlyOnRequest) { this.value$.next("-"); } else { - this.generate$.next(); + this.generate("autogenerate"); } }); }); @@ -472,7 +472,15 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { protected readonly userId$ = new BehaviorSubject(null); /** Emits when a new credential is requested */ - protected readonly generate$ = new Subject(); + private readonly generate$ = new Subject(); + + /** Request a new value from the generator + * @param requestor a label used to trace generation request + * origin in the debugger. + */ + protected generate(requestor: string) { + this.generate$.next(requestor); + } private toOptions(algorithms: AlgorithmInfo[]) { const options: Option[] = algorithms.map((algorithm) => ({ diff --git a/libs/tools/generator/components/src/forwarder-settings.component.ts b/libs/tools/generator/components/src/forwarder-settings.component.ts index a1e6c7acfd8..67e93c611ee 100644 --- a/libs/tools/generator/components/src/forwarder-settings.component.ts +++ b/libs/tools/generator/components/src/forwarder-settings.component.ts @@ -143,6 +143,8 @@ export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy control.clearValidators(); } } + + this.settings.updateValueAndValidity({ emitEvent: false }); }); // the first emission is the current value; subsequent emissions are updates diff --git a/libs/tools/generator/components/src/passphrase-settings.component.html b/libs/tools/generator/components/src/passphrase-settings.component.html index 2a3f4b5a287..25e9684e864 100644 --- a/libs/tools/generator/components/src/passphrase-settings.component.html +++ b/libs/tools/generator/components/src/passphrase-settings.component.html @@ -7,14 +7,7 @@
{{ "options" | i18n }}
{{ "numWords" | i18n }} - +
diff --git a/libs/tools/generator/components/src/passphrase-settings.component.ts b/libs/tools/generator/components/src/passphrase-settings.component.ts index 82524eba4d8..4c171e0c205 100644 --- a/libs/tools/generator/components/src/passphrase-settings.component.ts +++ b/libs/tools/generator/components/src/passphrase-settings.component.ts @@ -91,9 +91,8 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy { .get(Controls.wordSeparator) .setValidators(toValidators(Controls.wordSeparator, Generators.passphrase, constraints)); - // forward word boundaries to the template (can't do it through the rx form) - this.minNumWords = constraints.numWords.min; - this.maxNumWords = constraints.numWords.max; + this.settings.updateValueAndValidity({ emitEvent: false }); + this.policyInEffect = constraints.policyInEffect; this.toggleEnabled(Controls.capitalize, !constraints.capitalize?.readonly); @@ -104,12 +103,6 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy { this.settings.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(settings); } - /** attribute binding for numWords[min] */ - protected minNumWords: number; - - /** attribute binding for numWords[max] */ - protected maxNumWords: number; - /** display binding for enterprise policy notice */ protected policyInEffect: boolean; diff --git a/libs/tools/generator/components/src/password-generator.component.html b/libs/tools/generator/components/src/password-generator.component.html index aecdf0f6a4d..96aa8f00b1c 100644 --- a/libs/tools/generator/components/src/password-generator.component.html +++ b/libs/tools/generator/components/src/password-generator.component.html @@ -18,9 +18,11 @@ type="button" bitIconButton="bwi-generate" buttonType="main" - (click)="generate$.next()" + (click)="generate('user request')" [appA11yTitle]="credentialTypeGenerateLabel$ | async" - > + > + {{ credentialTypeGenerateLabel$ | async }} + + > + {{ credentialTypeGenerateLabel$ | async }} + + > + {{ credentialTypeCopyLabel$ | async }} + @@ -44,7 +48,7 @@

{{ "options" | i18n }}

{{ "options" | i18n }} diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts index ea75ef6079c..838177d030d 100644 --- a/libs/tools/generator/components/src/username-generator.component.ts +++ b/libs/tools/generator/components/src/username-generator.component.ts @@ -313,7 +313,7 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { if (!a || a.onlyOnRequest) { this.value$.next("-"); } else { - this.generate$.next(); + this.generate("autogenerate"); } }); }); @@ -391,7 +391,15 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { protected readonly userId$ = new BehaviorSubject(null); /** Emits when a new credential is requested */ - protected readonly generate$ = new Subject(); + private readonly generate$ = new Subject(); + + /** Request a new value from the generator + * @param requestor a label used to trace generation request + * origin in the debugger. + */ + protected generate(requestor: string) { + this.generate$.next(requestor); + } private toOptions(algorithms: AlgorithmInfo[]) { const options: Option[] = algorithms.map((algorithm) => ({ From a2a15d42d5c2501d70fed66e32f5b0210447d405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=A8=20Audrey=20=E2=9C=A8?= Date: Wed, 23 Oct 2024 14:58:49 -0400 Subject: [PATCH 052/126] add test ids (#11674) --- .../src/credential-generator.component.html | 14 ++++++++++++-- .../src/username-generator.component.html | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/libs/tools/generator/components/src/credential-generator.component.html b/libs/tools/generator/components/src/credential-generator.component.html index 06ea1f767b7..737e32fa1f9 100644 --- a/libs/tools/generator/components/src/credential-generator.component.html +++ b/libs/tools/generator/components/src/credential-generator.component.html @@ -56,7 +56,12 @@

{{ "options" | i18n }}

{{ "type" | i18n }} - + + {{ credentialTypeHint$ | async }} @@ -65,7 +70,12 @@

{{ "options" | i18n }}

{{ "service" | i18n }} - + + {{ "options" | i18n }}
{{ "type" | i18n }} - + + {{ credentialTypeHint$ | async }} @@ -42,7 +47,12 @@

{{ "options" | i18n }}

{{ "service" | i18n }} - + + Date: Wed, 23 Oct 2024 15:38:26 -0400 Subject: [PATCH 053/126] [PM-13723] track history in generator components (#11673) * add history support to generator components * increase generator history length --- .../src/credential-generator.component.ts | 11 ++++++- .../src/password-generator.component.ts | 31 ++++++++++++++++++- .../src/username-generator.component.ts | 11 ++++++- .../history/src/generated-credential.ts | 4 +-- .../src/generator-history.abstraction.ts | 4 +-- .../src/local-generator-history.service.ts | 12 +++++-- 6 files changed, 63 insertions(+), 10 deletions(-) diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts index a37de986499..e800ce4bd39 100644 --- a/libs/tools/generator/components/src/credential-generator.component.ts +++ b/libs/tools/generator/components/src/credential-generator.component.ts @@ -37,6 +37,7 @@ import { isUsernameAlgorithm, toCredentialGeneratorConfiguration, } from "@bitwarden/generator-core"; +import { GeneratorHistoryService } from "@bitwarden/generator-history"; // constants used to identify navigation selections that are not // generator algorithms @@ -51,6 +52,7 @@ const NONE_SELECTED = "none"; export class CredentialGeneratorComponent implements OnInit, OnDestroy { constructor( private generatorService: CredentialGeneratorService, + private generatorHistoryService: GeneratorHistoryService, private toastService: ToastService, private logService: LogService, private i18nService: I18nService, @@ -182,9 +184,16 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { // continue with origin stream return generator; }), + withLatestFrom(this.userId$), takeUntil(this.destroyed), ) - .subscribe((generated) => { + .subscribe(([generated, userId]) => { + this.generatorHistoryService + .track(userId, generated.credential, generated.category, generated.generationDate) + .catch((e: unknown) => { + this.logService.error(e); + }); + // update subjects within the angular zone so that the // template bindings refresh immediately this.zone.run(() => { diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts index 96af1b05c13..60c3f629538 100644 --- a/libs/tools/generator/components/src/password-generator.component.ts +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -2,6 +2,7 @@ import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core"; import { BehaviorSubject, + catchError, distinctUntilChanged, filter, map, @@ -14,7 +15,9 @@ import { 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 { UserId } from "@bitwarden/common/types/guid"; +import { ToastService } from "@bitwarden/components"; import { Option } from "@bitwarden/components/src/select/option"; import { CredentialGeneratorService, @@ -25,6 +28,7 @@ import { isPasswordAlgorithm, AlgorithmInfo, } from "@bitwarden/generator-core"; +import { GeneratorHistoryService } from "@bitwarden/generator-history"; /** Options group for passwords */ @Component({ @@ -34,6 +38,9 @@ import { export class PasswordGeneratorComponent implements OnInit, OnDestroy { constructor( private generatorService: CredentialGeneratorService, + private generatorHistoryService: GeneratorHistoryService, + private toastService: ToastService, + private logService: LogService, private i18nService: I18nService, private accountService: AccountService, private zone: NgZone, @@ -109,10 +116,32 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy { // wire up the generator this.algorithm$ .pipe( + filter((algorithm) => !!algorithm), switchMap((algorithm) => this.typeToGenerator$(algorithm.id)), + catchError((error: unknown, generator) => { + if (typeof error === "string") { + this.toastService.showToast({ + message: error, + variant: "error", + title: "", + }); + } else { + this.logService.error(error); + } + + // continue with origin stream + return generator; + }), + withLatestFrom(this.userId$), takeUntil(this.destroyed), ) - .subscribe((generated) => { + .subscribe(([generated, userId]) => { + this.generatorHistoryService + .track(userId, generated.credential, generated.category, generated.generationDate) + .catch((e: unknown) => { + this.logService.error(e); + }); + // update subjects within the angular zone so that the // template bindings refresh immediately this.zone.run(() => { diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts index 838177d030d..7ba4b254e98 100644 --- a/libs/tools/generator/components/src/username-generator.component.ts +++ b/libs/tools/generator/components/src/username-generator.component.ts @@ -36,6 +36,7 @@ import { isUsernameAlgorithm, toCredentialGeneratorConfiguration, } from "@bitwarden/generator-core"; +import { GeneratorHistoryService } from "@bitwarden/generator-history"; // constants used to identify navigation selections that are not // generator algorithms @@ -57,6 +58,7 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { */ constructor( private generatorService: CredentialGeneratorService, + private generatorHistoryService: GeneratorHistoryService, private toastService: ToastService, private logService: LogService, private i18nService: I18nService, @@ -153,9 +155,16 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { // continue with origin stream return generator; }), + withLatestFrom(this.userId$), takeUntil(this.destroyed), ) - .subscribe((generated) => { + .subscribe(([generated, userId]) => { + this.generatorHistoryService + .track(userId, generated.credential, generated.category, generated.generationDate) + .catch((e: unknown) => { + this.logService.error(e); + }); + // update subjects within the angular zone so that the // template bindings refresh immediately this.zone.run(() => { diff --git a/libs/tools/generator/extensions/history/src/generated-credential.ts b/libs/tools/generator/extensions/history/src/generated-credential.ts index 59a9623bf7e..32efb752258 100644 --- a/libs/tools/generator/extensions/history/src/generated-credential.ts +++ b/libs/tools/generator/extensions/history/src/generated-credential.ts @@ -1,6 +1,6 @@ import { Jsonify } from "type-fest"; -import { GeneratorCategory } from "./options"; +import { CredentialAlgorithm } from "@bitwarden/generator-core"; /** A credential generation result */ export class GeneratedCredential { @@ -14,7 +14,7 @@ export class GeneratedCredential { */ constructor( readonly credential: string, - readonly category: GeneratorCategory, + readonly category: CredentialAlgorithm, generationDate: Date | number, ) { if (typeof generationDate === "number") { diff --git a/libs/tools/generator/extensions/history/src/generator-history.abstraction.ts b/libs/tools/generator/extensions/history/src/generator-history.abstraction.ts index 78144c3043d..06d8741b2e0 100644 --- a/libs/tools/generator/extensions/history/src/generator-history.abstraction.ts +++ b/libs/tools/generator/extensions/history/src/generator-history.abstraction.ts @@ -1,9 +1,9 @@ import { Observable } from "rxjs"; import { UserId } from "@bitwarden/common/types/guid"; +import { CredentialAlgorithm } from "@bitwarden/generator-core"; import { GeneratedCredential } from "./generated-credential"; -import { GeneratorCategory } from "./options"; /** Tracks the history of password generations. * Each user gets their own store. @@ -27,7 +27,7 @@ export abstract class GeneratorHistoryService { track: ( userId: UserId, credential: string, - category: GeneratorCategory, + category: CredentialAlgorithm, date?: Date, ) => Promise; diff --git a/libs/tools/generator/extensions/history/src/local-generator-history.service.ts b/libs/tools/generator/extensions/history/src/local-generator-history.service.ts index 2416c84b63d..99497f7ad50 100644 --- a/libs/tools/generator/extensions/history/src/local-generator-history.service.ts +++ b/libs/tools/generator/extensions/history/src/local-generator-history.service.ts @@ -8,12 +8,13 @@ import { PaddedDataPacker } from "@bitwarden/common/tools/state/padded-data-pack import { SecretState } from "@bitwarden/common/tools/state/secret-state"; import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor"; import { UserId } from "@bitwarden/common/types/guid"; +import { CredentialAlgorithm } from "@bitwarden/generator-core"; import { GeneratedCredential } from "./generated-credential"; import { GeneratorHistoryService } from "./generator-history.abstraction"; import { GENERATOR_HISTORY, GENERATOR_HISTORY_BUFFER } from "./key-definitions"; import { LegacyPasswordHistoryDecryptor } from "./legacy-password-history-decryptor"; -import { GeneratorCategory, HistoryServiceOptions } from "./options"; +import { HistoryServiceOptions } from "./options"; const OPTIONS_FRAME_SIZE = 2048; @@ -25,7 +26,7 @@ export class LocalGeneratorHistoryService extends GeneratorHistoryService { private readonly encryptService: EncryptService, private readonly keyService: CryptoService, private readonly stateProvider: StateProvider, - private readonly options: HistoryServiceOptions = { maxTotal: 100 }, + private readonly options: HistoryServiceOptions = { maxTotal: 200 }, ) { super(); } @@ -33,7 +34,12 @@ export class LocalGeneratorHistoryService extends GeneratorHistoryService { private _credentialStates = new Map>(); /** {@link GeneratorHistoryService.track} */ - track = async (userId: UserId, credential: string, category: GeneratorCategory, date?: Date) => { + track = async ( + userId: UserId, + credential: string, + category: CredentialAlgorithm, + date?: Date, + ) => { const state = this.getCredentialState(userId); let result: GeneratedCredential = null; From 9b471e663336af838f65eb2486dfbcbf922cb940 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 24 Oct 2024 08:22:43 -0500 Subject: [PATCH 054/126] [PM-13715] Launching a website from the extension does not trigger an update to reference the correct autofill value (#11587) * [PM-13715] Launching page from cipher does not set correct autofill action * [PM-13715] Fix autofill not triggering for correct cipher after page has been launched from browser extension --- .../popup/components/vault/view.component.html | 2 +- libs/angular/src/vault/components/view.component.ts | 6 ++---- libs/common/src/vault/services/cipher.service.ts | 13 +++++-------- .../autofill-options-view.component.ts | 10 ++++++++-- .../src/cipher-view/cipher-view.component.html | 6 +++++- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault/view.component.html b/apps/browser/src/vault/popup/components/vault/view.component.html index ff76c68464d..73415c9070a 100644 --- a/apps/browser/src/vault/popup/components/vault/view.component.html +++ b/apps/browser/src/vault/popup/components/vault/view.component.html @@ -472,7 +472,7 @@

attr.aria-label="{{ 'launch' | i18n }} {{ u.uri }}" appA11yTitle="{{ 'launch' | i18n }}" *ngIf="u.canLaunch" - (click)="launch(u)" + (click)="launch(u, cipher.id)" > diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index 4c96c10dac3..2ff34ebafa5 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -348,15 +348,13 @@ export class ViewComponent implements OnDestroy, OnInit { } } - launch(uri: Launchable, cipherId?: string) { + async launch(uri: Launchable, cipherId?: string) { if (!uri.canLaunch) { return; } if (cipherId) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.cipherService.updateLastLaunchedDate(cipherId); + await this.cipherService.updateLastLaunchedDate(cipherId); } this.platformUtilsService.launchUri(uri.launchUri); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index a7377a93eec..207a5da3cbf 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -650,14 +650,11 @@ export class CipherService implements CipherServiceAbstraction { ciphersLocalData = {}; } - const cipherId = id as CipherId; - if (ciphersLocalData[cipherId]) { - ciphersLocalData[cipherId].lastLaunched = new Date().getTime(); - } else { - ciphersLocalData[cipherId] = { - lastUsedDate: new Date().getTime(), - }; - } + const currentTime = new Date().getTime(); + ciphersLocalData[id as CipherId] = { + lastLaunched: currentTime, + lastUsedDate: currentTime, + }; await this.localDataState.update(() => ciphersLocalData); diff --git a/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts b/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts index b7708b5aa98..2c3739dba41 100644 --- a/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts +++ b/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts @@ -3,6 +3,7 @@ import { Component, Input } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { CardComponent, @@ -30,10 +31,15 @@ import { }) export class AutofillOptionsViewComponent { @Input() loginUris: LoginUriView[]; + @Input() cipherId: string; - constructor(private platformUtilsService: PlatformUtilsService) {} + constructor( + private platformUtilsService: PlatformUtilsService, + private cipherService: CipherService, + ) {} - openWebsite(selectedUri: string) { + async openWebsite(selectedUri: string) { + await this.cipherService.updateLastLaunchedDate(this.cipherId); this.platformUtilsService.launchUri(selectedUri); } } diff --git a/libs/vault/src/cipher-view/cipher-view.component.html b/libs/vault/src/cipher-view/cipher-view.component.html index b693c448158..2dd98092cbf 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.html +++ b/libs/vault/src/cipher-view/cipher-view.component.html @@ -25,7 +25,11 @@ - + From b3b311e164bf677d3b589cb0a72fe6968044610b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 24 Oct 2024 15:43:49 +0200 Subject: [PATCH 055/126] Add logging for decryption failures (#11683) * Add logging to decryption routines * Fix case of uknown encryption type * Remove enum to string mapping --- .../platform/enums/encryption-type.enum.ts | 8 +++ .../encrypt.service.implementation.ts | 59 +++++++++++++++---- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/libs/common/src/platform/enums/encryption-type.enum.ts b/libs/common/src/platform/enums/encryption-type.enum.ts index b4ecd780499..a0ffe679279 100644 --- a/libs/common/src/platform/enums/encryption-type.enum.ts +++ b/libs/common/src/platform/enums/encryption-type.enum.ts @@ -8,6 +8,14 @@ export enum EncryptionType { Rsa2048_OaepSha1_HmacSha256_B64 = 6, } +export function encryptionTypeToString(encryptionType: EncryptionType): string { + if (encryptionType in EncryptionType) { + return EncryptionType[encryptionType]; + } else { + return "Unknown encryption type " + encryptionType; + } +} + /** The expected number of parts to a serialized EncString of the given encryption type. * For example, an EncString of type AesCbc256_B64 will have 2 parts, and an EncString of type * AesCbc128_HmacSha256_B64 will have 3 parts. diff --git a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts b/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts index 681972e7e4b..8c42c724b24 100644 --- a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts +++ b/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts @@ -2,7 +2,7 @@ import { Utils } from "../../../platform/misc/utils"; import { CryptoFunctionService } from "../../abstractions/crypto-function.service"; import { EncryptService } from "../../abstractions/encrypt.service"; import { LogService } from "../../abstractions/log.service"; -import { EncryptionType } from "../../enums"; +import { EncryptionType, encryptionTypeToString as encryptionTypeName } from "../../enums"; import { Decryptable } from "../../interfaces/decryptable.interface"; import { Encrypted } from "../../interfaces/encrypted"; import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface"; @@ -70,13 +70,24 @@ export class EncryptServiceImplementation implements EncryptService { key = this.resolveLegacyKey(key, encString); + // DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality. if (key.macKey != null && encString?.mac == null) { - this.logService.error("MAC required but not provided."); + this.logService.error( + "[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encString.encryptionType), + ); return null; } if (key.encType !== encString.encryptionType) { - this.logService.error("Key encryption type does not match payload encryption type."); + this.logService.error( + "[Encrypt service] Key encryption type does not match payload encryption type. Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encString.encryptionType), + ); return null; } @@ -94,7 +105,12 @@ export class EncryptServiceImplementation implements EncryptService { ); const macsEqual = await this.cryptoFunctionService.compareFast(fastParams.mac, computedMac); if (!macsEqual) { - this.logMacFailed("MAC comparison failed. Key or payload has changed."); + this.logMacFailed( + "[Encrypt service] MAC comparison failed. Key or payload has changed. Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encString.encryptionType), + ); return null; } } @@ -113,13 +129,24 @@ export class EncryptServiceImplementation implements EncryptService { key = this.resolveLegacyKey(key, encThing); + // DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality. if (key.macKey != null && encThing.macBytes == null) { - this.logService.error("MAC required but not provided."); + this.logService.error( + "[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encThing.encryptionType), + ); return null; } if (key.encType !== encThing.encryptionType) { - this.logService.error("Key encryption type does not match payload encryption type."); + this.logService.error( + "[Encrypt service] Key encryption type does not match payload encryption type. Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encThing.encryptionType), + ); return null; } @@ -129,13 +156,25 @@ export class EncryptServiceImplementation implements EncryptService { macData.set(new Uint8Array(encThing.dataBytes), encThing.ivBytes.byteLength); const computedMac = await this.cryptoFunctionService.hmac(macData, key.macKey, "sha256"); if (computedMac === null) { - this.logMacFailed("Failed to compute MAC."); + this.logMacFailed( + "[Encrypt service] Failed to compute MAC." + + " Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encThing.encryptionType), + ); return null; } const macsMatch = await this.cryptoFunctionService.compare(encThing.macBytes, computedMac); if (!macsMatch) { - this.logMacFailed("MAC comparison failed. Key or payload has changed."); + this.logMacFailed( + "[Encrypt service] MAC comparison failed. Key or payload has changed." + + " Key type " + + encryptionTypeName(key.encType) + + " Payload type " + + encryptionTypeName(encThing.encryptionType), + ); return null; } } @@ -164,7 +203,7 @@ export class EncryptServiceImplementation implements EncryptService { async rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise { if (data == null) { - throw new Error("No data provided for decryption."); + throw new Error("[Encrypt service] rsaDecrypt: No data provided for decryption."); } let algorithm: "sha1" | "sha256"; @@ -182,7 +221,7 @@ export class EncryptServiceImplementation implements EncryptService { } if (privateKey == null) { - throw new Error("No private key provided for decryption."); + throw new Error("[Encrypt service] rsaDecrypt: No private key provided for decryption."); } return this.cryptoFunctionService.rsaDecrypt(data.dataBytes, privateKey, algorithm); From 15c301d39f94b71b08a6310c88b036f51609d4d9 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Thu, 24 Oct 2024 10:15:24 -0400 Subject: [PATCH 056/126] Do not redirect after saving changes to excluded domains (#11676) --- .../settings/excluded-domains.component.html | 10 ++- .../settings/excluded-domains.component.ts | 81 ++++++++++++------- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/apps/browser/src/autofill/popup/settings/excluded-domains.component.html b/apps/browser/src/autofill/popup/settings/excluded-domains.component.html index 1dec438fdd2..e3b6bf5f802 100644 --- a/apps/browser/src/autofill/popup/settings/excluded-domains.component.html +++ b/apps/browser/src/autofill/popup/settings/excluded-domains.component.html @@ -11,7 +11,7 @@ accountSwitcherEnabled ? ("excludedDomainsDescAlt" | i18n) : ("excludedDomainsDesc" | i18n) }}

- +

{{ "domainsTitle" | i18n }}

{{ excludedDomainsState?.length || 0 }} @@ -57,7 +57,13 @@

{{ "domainsTitle" | i18n }}

- diff --git a/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts b/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts index 381ba903423..f622312ce04 100644 --- a/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts +++ b/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts @@ -1,13 +1,19 @@ import { CommonModule } from "@angular/common"; -import { QueryList, Component, ElementRef, OnDestroy, OnInit, ViewChildren } from "@angular/core"; +import { + QueryList, + Component, + ElementRef, + OnDestroy, + AfterViewInit, + ViewChildren, +} from "@angular/core"; import { FormsModule } from "@angular/forms"; -import { Router, RouterModule } from "@angular/router"; -import { firstValueFrom, Subject, takeUntil } from "rxjs"; +import { RouterModule } from "@angular/router"; +import { Subject, takeUntil } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { NeverDomains } from "@bitwarden/common/models/domain/domain-service"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -29,8 +35,6 @@ import { PopupFooterComponent } from "../../../platform/popup/layout/popup-foote import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; -const BroadcasterSubscriptionId = "excludedDomainsState"; - @Component({ selector: "app-excluded-domains", templateUrl: "excluded-domains.component.html", @@ -55,11 +59,12 @@ const BroadcasterSubscriptionId = "excludedDomainsState"; TypographyModule, ], }) -export class ExcludedDomainsComponent implements OnInit, OnDestroy { +export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy { @ViewChildren("uriInput") uriInputElements: QueryList>; accountSwitcherEnabled = false; dataIsPristine = true; + isLoading = false; excludedDomainsState: string[] = []; storedExcludedDomains: string[] = []; // How many fields should be non-editable before editable fields @@ -70,16 +75,27 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { constructor( private domainSettingsService: DomainSettingsService, private i18nService: I18nService, - private router: Router, - private broadcasterService: BroadcasterService, private platformUtilsService: PlatformUtilsService, ) { this.accountSwitcherEnabled = enableAccountSwitching(); } - async ngOnInit() { - const neverDomains = await firstValueFrom(this.domainSettingsService.neverDomains$); + async ngAfterViewInit() { + this.domainSettingsService.neverDomains$ + .pipe(takeUntil(this.destroy$)) + .subscribe((neverDomains: NeverDomains) => this.handleStateUpdate(neverDomains)); + + this.uriInputElements.changes.pipe(takeUntil(this.destroy$)).subscribe(({ last }) => { + this.focusNewUriInput(last); + }); + } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + handleStateUpdate(neverDomains: NeverDomains) { if (neverDomains) { this.storedExcludedDomains = Object.keys(neverDomains); } @@ -89,15 +105,8 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { // Do not allow the first x (pre-existing) fields to be edited this.fieldsEditThreshold = this.storedExcludedDomains.length; - this.uriInputElements.changes.pipe(takeUntil(this.destroy$)).subscribe(({ last }) => { - this.focusNewUriInput(last); - }); - } - - ngOnDestroy() { - this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); - this.destroy$.next(); - this.destroy$.complete(); + this.dataIsPristine = true; + this.isLoading = false; } focusNewUriInput(elementRef: ElementRef) { @@ -116,7 +125,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { async removeDomain(i: number) { this.excludedDomainsState.splice(i, 1); - // if a pre-existing field was dropped, lower the edit threshold + // If a pre-existing field was dropped, lower the edit threshold if (i < this.fieldsEditThreshold) { this.fieldsEditThreshold--; } @@ -132,11 +141,11 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { async saveChanges() { if (this.dataIsPristine) { - await this.router.navigate(["/notifications"]); - return; } + this.isLoading = true; + const newExcludedDomainsSaveState: NeverDomains = {}; const uniqueExcludedDomains = new Set(this.excludedDomainsState); @@ -151,6 +160,8 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { this.i18nService.t("excludedDomainsInvalidDomain", uri), ); + // Don't reset via `handleStateUpdate` to allow existing input value correction + this.isLoading = false; return; } @@ -159,7 +170,23 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { } try { - await this.domainSettingsService.setNeverDomains(newExcludedDomainsSaveState); + const existingState = new Set(this.storedExcludedDomains); + const newState = new Set(Object.keys(newExcludedDomainsSaveState)); + const stateIsUnchanged = + existingState.size === newState.size && + new Set([...existingState, ...newState]).size === existingState.size; + + // The subscriber updates don't trigger if `setNeverDomains` sets an equivalent state + if (stateIsUnchanged) { + // Reset UI state directly + const constructedNeverDomainsState = this.storedExcludedDomains.reduce( + (neverDomains, uri) => ({ ...neverDomains, [uri]: null }), + {}, + ); + this.handleStateUpdate(constructedNeverDomainsState); + } else { + await this.domainSettingsService.setNeverDomains(newExcludedDomainsSaveState); + } this.platformUtilsService.showToast( "success", @@ -169,11 +196,9 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { } catch { this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); - // Do not navigate on error - return; + // Don't reset via `handleStateUpdate` to preserve input values + this.isLoading = false; } - - await this.router.navigate(["/notifications"]); } trackByFunction(index: number, _: string) { From 548abfe906acdab3b6408c4efdd0179bb2baac26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:39:41 +0100 Subject: [PATCH 057/126] [PM-12758] Add managed status to OrganizationUserDetailsResponse, OrganizationUserUserDetailsResponse, OrganizationUserView and OrganizationUserAdminView (#11640) * Add managedByOrganization property to OrganizationUserUserDetailsResponse and OrganizationUserView * Add managedByOrganization property to OrganizationUserDetailsResponse and OrganizationUserAdminView * Move response mapping from UserAdminService to method in OrganizationUserAdminView --- .../core/services/user-admin.service.ts | 42 +------------------ .../views/organization-user-admin-view.ts | 34 ++++++++++++++- .../core/views/organization-user.view.ts | 1 + .../responses/organization-user.response.ts | 5 +++ 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/core/services/user-admin.service.ts b/apps/web/src/app/admin-console/organizations/core/services/user-admin.service.ts index 9741758e1e0..61fc27614d1 100644 --- a/apps/web/src/app/admin-console/organizations/core/services/user-admin.service.ts +++ b/apps/web/src/app/admin-console/organizations/core/services/user-admin.service.ts @@ -4,19 +4,14 @@ import { OrganizationUserApiService, OrganizationUserInviteRequest, OrganizationUserUpdateRequest, - OrganizationUserDetailsResponse, } from "@bitwarden/admin-console/common"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CoreOrganizationModule } from "../core-organization.module"; import { OrganizationUserAdminView } from "../views/organization-user-admin-view"; @Injectable({ providedIn: CoreOrganizationModule }) export class UserAdminService { - constructor( - private configService: ConfigService, - private organizationUserApiService: OrganizationUserApiService, - ) {} + constructor(private organizationUserApiService: OrganizationUserApiService) {} async get( organizationId: string, @@ -34,9 +29,7 @@ export class UserAdminService { return undefined; } - const [view] = await this.decryptMany(organizationId, [userResponse]); - - return view; + return OrganizationUserAdminView.fromResponse(organizationId, userResponse); } async save(user: OrganizationUserAdminView): Promise { @@ -65,35 +58,4 @@ export class UserAdminService { await this.organizationUserApiService.postOrganizationUserInvite(user.organizationId, request); } - - private async decryptMany( - organizationId: string, - users: OrganizationUserDetailsResponse[], - ): Promise { - const promises = users.map(async (u) => { - const view = new OrganizationUserAdminView(); - - view.id = u.id; - view.organizationId = organizationId; - view.userId = u.userId; - view.type = u.type; - view.status = u.status; - view.externalId = u.externalId; - view.permissions = u.permissions; - view.resetPasswordEnrolled = u.resetPasswordEnrolled; - view.collections = u.collections.map((c) => ({ - id: c.id, - hidePasswords: c.hidePasswords, - readOnly: c.readOnly, - manage: c.manage, - })); - view.groups = u.groups; - view.accessSecretsManager = u.accessSecretsManager; - view.hasMasterPassword = u.hasMasterPassword; - - return view; - }); - - return await Promise.all(promises); - } } diff --git a/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts b/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts index b9b034b405d..63a8c938e24 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts @@ -1,4 +1,7 @@ -import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; +import { + CollectionAccessSelectionView, + OrganizationUserDetailsResponse, +} from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType, OrganizationUserType, @@ -15,9 +18,38 @@ export class OrganizationUserAdminView { permissions: PermissionsApi; resetPasswordEnrolled: boolean; hasMasterPassword: boolean; + managedByOrganization: boolean; collections: CollectionAccessSelectionView[] = []; groups: string[] = []; accessSecretsManager: boolean; + + static fromResponse( + organizationId: string, + response: OrganizationUserDetailsResponse, + ): OrganizationUserAdminView { + const view = new OrganizationUserAdminView(); + + view.id = response.id; + view.organizationId = organizationId; + view.userId = response.userId; + view.type = response.type; + view.status = response.status; + view.externalId = response.externalId; + view.permissions = response.permissions; + view.resetPasswordEnrolled = response.resetPasswordEnrolled; + view.collections = response.collections.map((c) => ({ + id: c.id, + hidePasswords: c.hidePasswords, + readOnly: c.readOnly, + manage: c.manage, + })); + view.groups = response.groups; + view.accessSecretsManager = response.accessSecretsManager; + view.hasMasterPassword = response.hasMasterPassword; + view.managedByOrganization = response.managedByOrganization; + + return view; + } } diff --git a/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts b/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts index 7d1a10c5332..eac80dd0242 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts @@ -25,6 +25,7 @@ export class OrganizationUserView { * True if this organizaztion user has been granted access to Secrets Manager, false otherwise. */ accessSecretsManager: boolean; + managedByOrganization: boolean; collections: CollectionAccessSelectionView[] = []; groups: string[] = []; diff --git a/libs/admin-console/src/common/organization-user/models/responses/organization-user.response.ts b/libs/admin-console/src/common/organization-user/models/responses/organization-user.response.ts index 7323855f69f..f61d9325c2a 100644 --- a/libs/admin-console/src/common/organization-user/models/responses/organization-user.response.ts +++ b/libs/admin-console/src/common/organization-user/models/responses/organization-user.response.ts @@ -49,6 +49,7 @@ export class OrganizationUserUserDetailsResponse extends OrganizationUserRespons avatarColor: string; twoFactorEnabled: boolean; usesKeyConnector: boolean; + managedByOrganization: boolean; constructor(response: any) { super(response); @@ -57,12 +58,16 @@ export class OrganizationUserUserDetailsResponse extends OrganizationUserRespons this.avatarColor = this.getResponseProperty("AvatarColor"); this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled"); this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false; + this.managedByOrganization = this.getResponseProperty("ManagedByOrganization") ?? false; } } export class OrganizationUserDetailsResponse extends OrganizationUserResponse { + managedByOrganization: boolean; + constructor(response: any) { super(response); + this.managedByOrganization = this.getResponseProperty("ManagedByOrganization") ?? false; } } From 44e182e32ece3af4234a3d9173a9b98e740870d3 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:30:46 -0500 Subject: [PATCH 058/126] [PM-13187] Hide "Assign To Collections" when the user has no orgs (#11668) * web - hide assign to collections button when the user has no organizations * browser - hide assign to collections button when the user has no organizations * hide assign to collections in the bulk edit menu when the user doesn't belong to an organization --- .../item-more-options.component.html | 2 +- .../item-more-options.component.ts | 13 +++++++++++-- .../vault-items/vault-cipher-row.component.ts | 2 +- .../components/vault-items/vault-items.component.ts | 5 +++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 8374bb0cc79..03287c75fa9 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -28,7 +28,7 @@ {{ "clone" | i18n }} - + {{ "assignToCollections" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index 2bc3fcea2f9..8c52ee7ed84 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -1,9 +1,10 @@ import { CommonModule } from "@angular/common"; -import { booleanAttribute, Component, Input } from "@angular/core"; +import { booleanAttribute, Component, Input, OnInit } from "@angular/core"; import { Router, RouterModule } from "@angular/router"; import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -29,7 +30,7 @@ import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; templateUrl: "./item-more-options.component.html", imports: [ItemModule, IconButtonModule, MenuModule, CommonModule, JslibModule, RouterModule], }) -export class ItemMoreOptionsComponent { +export class ItemMoreOptionsComponent implements OnInit { @Input({ required: true, }) @@ -44,6 +45,9 @@ export class ItemMoreOptionsComponent { protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$; + /** Boolean dependent on the current user having access to an organization */ + protected hasOrganizations = false; + constructor( private cipherService: CipherService, private passwordRepromptService: PasswordRepromptService, @@ -53,8 +57,13 @@ export class ItemMoreOptionsComponent { private i18nService: I18nService, private vaultPopupAutofillService: VaultPopupAutofillService, private accountService: AccountService, + private organizationService: OrganizationService, ) {} + async ngOnInit(): Promise { + this.hasOrganizations = await this.organizationService.hasOrganizations(); + } + get canEdit() { return this.cipher.edit; } diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts index 67b02d364f5..355f0240ece 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts @@ -92,7 +92,7 @@ export class VaultCipherRowComponent implements OnInit { } protected get showAssignToCollections() { - return this.canEditCipher && !this.cipher.isDeleted; + return this.organizations?.length && this.canEditCipher && !this.cipher.isDeleted; } protected get showClone() { diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index f469cec75a0..db1c2d6b567 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -299,6 +299,11 @@ export class VaultItemsComponent { return false; } + // When the user doesn't belong to an organization, hide assign to collections + if (this.allOrganizations.length === 0) { + return false; + } + if (this.selection.selected.length === 0) { return true; } From ed4071c7d463ab252017d10e45732b6a528db53b Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:06:08 -0400 Subject: [PATCH 059/126] Revert [PM-11312] Add "prevent screenshot" setting (#11685) This reverts commit 1b7bb014d203d23e9ac391382941a563cf0132b7. --- .../src/app/accounts/settings.component.html | 17 ----------------- .../src/app/accounts/settings.component.ts | 6 ------ apps/desktop/src/locales/en/messages.json | 6 ------ apps/desktop/src/main/window.main.ts | 15 --------------- .../services/desktop-settings.service.ts | 19 ------------------- 5 files changed, 63 deletions(-) diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html index 02b64a757a7..76cf98b1b24 100644 --- a/apps/desktop/src/app/accounts/settings.component.html +++ b/apps/desktop/src/app/accounts/settings.component.html @@ -419,23 +419,6 @@

"enableHardwareAccelerationDesc" | i18n }} -
-
- -
- {{ - "allowScreenshotsDesc" | i18n - }} -
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts index 30effa880e7..3b38cc4763b 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/accept-provider.component.ts @@ -1,5 +1,6 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Params, Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; import { RegisterRouteService } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -15,6 +16,9 @@ import { BaseAcceptComponent } from "@bitwarden/web-vault/app/common/base.accept }) export class AcceptProviderComponent extends BaseAcceptComponent { providerName: string; + providerId: string; + providerUserId: string; + providerInviteToken: string; failedMessage = "providerInviteAcceptFailed"; @@ -52,5 +56,31 @@ export class AcceptProviderComponent extends BaseAcceptComponent { async unauthedHandler(qParams: Params) { this.providerName = qParams.providerName; + this.providerId = qParams.providerId; + this.providerUserId = qParams.providerUserId; + this.providerInviteToken = qParams.token; + } + + async register() { + let queryParams: Params; + let registerRoute = await firstValueFrom(this.registerRoute$); + if (registerRoute === "/register") { + queryParams = { + email: this.email, + }; + } else if (registerRoute === "/signup") { + // We have to override the base component route as we don't need users to + // complete email verification if they are coming directly an emailed invite. + registerRoute = "/finish-signup"; + queryParams = { + email: this.email, + providerUserId: this.providerUserId, + providerInviteToken: this.providerInviteToken, + }; + } + + await this.router.navigate([registerRoute], { + queryParams: queryParams, + }); } } diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts index bf0e75ed333..e034e23de43 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.spec.ts @@ -113,8 +113,14 @@ describe("DefaultRegistrationFinishService", () => { kdfIterations: passwordInputResult.kdfConfig.iterations, kdfMemory: undefined, kdfParallelism: undefined, - orgInviteToken: undefined, // OrgInvite only handled in web - organizationUserId: undefined, // OrgInvite only handled in web + // Web only fields should be undefined + orgInviteToken: undefined, + organizationUserId: undefined, + orgSponsoredFreeFamilyPlanToken: undefined, + acceptEmergencyAccessInviteToken: undefined, + acceptEmergencyAccessId: undefined, + providerInviteToken: undefined, + providerUserId: undefined, }), ); }); diff --git a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts index 2443f774646..f2c4d4c98b6 100644 --- a/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/default-registration-finish.service.ts @@ -30,6 +30,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi orgSponsoredFreeFamilyPlanToken?: string, acceptEmergencyAccessInviteToken?: string, emergencyAccessId?: string, + providerInviteToken?: string, + providerUserId?: string, ): Promise { const [newUserKey, newEncUserKey] = await this.keyService.makeUserKey( passwordInputResult.masterKey, @@ -49,6 +51,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi orgSponsoredFreeFamilyPlanToken, acceptEmergencyAccessInviteToken, emergencyAccessId, + providerInviteToken, + providerUserId, ); const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest); @@ -65,6 +69,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi orgSponsoredFreeFamilyPlanToken?: string, // web only acceptEmergencyAccessInviteToken?: string, // web only emergencyAccessId?: string, // web only + providerInviteToken?: string, // web only + providerUserId?: string, // web only ): Promise { const userAsymmetricKeysRequest = new KeysRequest( userAsymmetricKeys[0], 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 3a6d26ef939..7cfa85ec3d4 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 @@ -49,6 +49,10 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { acceptEmergencyAccessInviteToken: string; emergencyAccessId: string; + // This token is provided when the user is coming from an emailed invite to accept a provider invite + providerInviteToken: string; + providerUserId: string; + masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null; constructor( @@ -104,6 +108,11 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { this.acceptEmergencyAccessInviteToken = qParams.acceptEmergencyAccessInviteToken; this.emergencyAccessId = qParams.emergencyAccessId; } + + if (qParams.providerInviteToken != null && qParams.providerUserId != null) { + this.providerInviteToken = qParams.providerInviteToken; + this.providerUserId = qParams.providerUserId; + } } private async initOrgInviteFlowIfPresent(): Promise { @@ -140,6 +149,8 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { this.orgSponsoredFreeFamilyPlanToken, this.acceptEmergencyAccessInviteToken, this.emergencyAccessId, + this.providerInviteToken, + this.providerUserId, ); } catch (e) { this.validationService.showError(e); diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts index b7abd381084..3746e37b84a 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.service.ts @@ -25,6 +25,8 @@ export abstract class RegistrationFinishService { * @param orgSponsoredFreeFamilyPlanToken The optional org sponsored free family plan token. * @param acceptEmergencyAccessInviteToken The optional accept emergency access invite token. * @param emergencyAccessId The optional emergency access id which is required to validate the emergency access invite token. + * @param providerInviteToken The optional provider invite token. + * @param providerUserId The optional provider user id which is required to validate the provider invite token. * @returns a promise which resolves to the captcha bypass token string upon a successful account creation. */ abstract finishRegistration( @@ -34,5 +36,7 @@ export abstract class RegistrationFinishService { orgSponsoredFreeFamilyPlanToken?: string, acceptEmergencyAccessInviteToken?: string, emergencyAccessId?: string, + providerInviteToken?: string, + providerUserId?: string, ): Promise; } diff --git a/libs/common/src/auth/models/request/registration/register-finish.request.ts b/libs/common/src/auth/models/request/registration/register-finish.request.ts index 6a36bf82139..7ffac6bfe6c 100644 --- a/libs/common/src/auth/models/request/registration/register-finish.request.ts +++ b/libs/common/src/auth/models/request/registration/register-finish.request.ts @@ -21,6 +21,8 @@ export class RegisterFinishRequest { public orgSponsoredFreeFamilyPlanToken?: string, public acceptEmergencyAccessInviteToken?: string, public acceptEmergencyAccessId?: string, + public providerInviteToken?: string, + public providerUserId?: string, // Org Invite data (only applies on web) public organizationUserId?: string, From 877d379f8634227eef157b13e08a3884f6da7209 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:02:10 -0400 Subject: [PATCH 073/126] Auth/PM-8111 - LoginComponent Refactor Bug - Fix Extension SSO (#11699) * PM-8111 - LoginComponent Refactor - I broke the browser SSO flow - fixing it as clientId doesn't persist unless it is in state qParam. * PM-8111 - Fix DefaultLoginComponentService tests --- apps/web/src/connectors/sso.ts | 3 +-- .../angular/login/default-login-component.service.spec.ts | 8 +++++++- .../src/angular/login/default-login-component.service.ts | 7 ++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/web/src/connectors/sso.ts b/apps/web/src/connectors/sso.ts index 44ead1dc184..e049c64e5d9 100644 --- a/apps/web/src/connectors/sso.ts +++ b/apps/web/src/connectors/sso.ts @@ -6,11 +6,10 @@ window.addEventListener("load", () => { const code = getQsParam("code"); const state = getQsParam("state"); const lastpass = getQsParam("lp"); - const clientId = getQsParam("clientId"); if (lastpass === "1") { initiateBrowserSso(code, state, true); - } else if (state != null && clientId == "browser") { + } else if (state != null && state.includes(":clientId=browser")) { initiateBrowserSso(code, state, false); } else { window.location.href = window.location.origin + "/#/sso?code=" + code + "&state=" + state; diff --git a/libs/auth/src/angular/login/default-login-component.service.spec.ts b/libs/auth/src/angular/login/default-login-component.service.spec.ts index 2b565ea67b5..9878372801b 100644 --- a/libs/auth/src/angular/login/default-login-component.service.spec.ts +++ b/libs/auth/src/angular/login/default-login-component.service.spec.ts @@ -83,12 +83,14 @@ describe("DefaultLoginComponentService", () => { describe("launchSsoBrowserWindow", () => { const email = "test@bitwarden.com"; - const state = "testState"; + let state = "testState"; const codeVerifier = "testCodeVerifier"; const codeChallenge = "testCodeChallenge"; const baseUrl = "https://webvault.bitwarden.com/#/sso"; beforeEach(() => { + state = "testState"; + passwordGenerationService.generatePassword.mockResolvedValueOnce(state); passwordGenerationService.generatePassword.mockResolvedValueOnce(codeVerifier); jest.spyOn(Utils, "fromBufferToUrlB64").mockReturnValue(codeChallenge); @@ -112,6 +114,10 @@ describe("DefaultLoginComponentService", () => { await service.launchSsoBrowserWindow(email, clientId as "browser" | "desktop"); + if (clientType === ClientType.Browser) { + state += ":clientId=browser"; + } + const expectedUrl = `${baseUrl}?clientId=${clientId}&redirectUri=${encodeURIComponent(expectedRedirectUri)}&state=${state}&codeChallenge=${codeChallenge}&email=${encodeURIComponent(email)}`; expect(ssoLoginService.setSsoEmail).toHaveBeenCalledWith(email); diff --git a/libs/auth/src/angular/login/default-login-component.service.ts b/libs/auth/src/angular/login/default-login-component.service.ts index 30ab55cc0e9..a06ad1e6539 100644 --- a/libs/auth/src/angular/login/default-login-component.service.ts +++ b/libs/auth/src/angular/login/default-login-component.service.ts @@ -50,7 +50,12 @@ export class DefaultLoginComponentService implements LoginComponentService { special: false, }; - const state = await this.passwordGenerationService.generatePassword(passwordOptions); + let state = await this.passwordGenerationService.generatePassword(passwordOptions); + + if (clientId === "browser") { + // Need to persist the clientId in the state for the extension + state += ":clientId=browser"; + } const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, "sha256"); From 55cd4352790b6c865ae7f7db356148c3d413b0f3 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:16:59 +1000 Subject: [PATCH 074/126] [PM-13790] Remove RefactorOrganizationUserApi feature flag (#11662) --- .../src/service-container/service-container.ts | 5 +---- .../default-organization-user-api.service.ts | 17 +---------------- .../src/services/jslib-services.module.ts | 2 +- libs/common/src/enums/feature-flag.enum.ts | 2 -- 4 files changed, 3 insertions(+), 23 deletions(-) diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 48f4e17b340..3424e3fb28c 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -810,10 +810,7 @@ export class ServiceContainer { this.providerApiService = new ProviderApiService(this.apiService); - this.organizationUserApiService = new DefaultOrganizationUserApiService( - this.apiService, - this.configService, - ); + this.organizationUserApiService = new DefaultOrganizationUserApiService(this.apiService); this.cipherAuthorizationService = new DefaultCipherAuthorizationService( this.collectionService, diff --git a/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts b/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts index a6438b8b5ff..6a9911e732c 100644 --- a/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts +++ b/libs/admin-console/src/common/organization-user/services/default-organization-user-api.service.ts @@ -1,9 +1,5 @@ -import { firstValueFrom } from "rxjs"; - import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { OrganizationUserApiService } from "../abstractions"; import { @@ -27,10 +23,7 @@ import { } from "../models/responses"; export class DefaultOrganizationUserApiService implements OrganizationUserApiService { - constructor( - private apiService: ApiService, - private configService: ConfigService, - ) {} + constructor(private apiService: ApiService) {} async getOrganizationUser( organizationId: string, @@ -95,14 +88,6 @@ export class DefaultOrganizationUserApiService implements OrganizationUserApiSer async getAllMiniUserDetails( organizationId: string, ): Promise> { - const apiEnabled = await firstValueFrom( - this.configService.getFeatureFlag$(FeatureFlag.Pm3478RefactorOrganizationUserApi), - ); - if (!apiEnabled) { - // Keep using the old api until this feature flag is enabled - return this.getAllUsers(organizationId); - } - const r = await this.apiService.send( "GET", `/organizations/${organizationId}/users/mini-details`, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 444fb55deec..5d8822866d6 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -972,7 +972,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: OrganizationUserApiService, useClass: DefaultOrganizationUserApiService, - deps: [ApiServiceAbstraction, ConfigService], + deps: [ApiServiceAbstraction], }), safeProvider({ provide: PasswordResetEnrollmentServiceAbstraction, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 905d7299489..84cf5ed521e 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -33,7 +33,6 @@ export enum FeatureFlag { CipherKeyEncryption = "cipher-key-encryption", VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint", PM11901_RefactorSelfHostingLicenseUploader = "PM-11901-refactor-self-hosting-license-uploader", - Pm3478RefactorOrganizationUserApi = "pm-3478-refactor-organizationuser-api", AccessIntelligence = "pm-13227-access-intelligence", Pm13322AddPolicyDefinitions = "pm-13322-add-policy-definitions", LimitCollectionCreationDeletionSplit = "pm-10863-limit-collection-creation-deletion-split", @@ -80,7 +79,6 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE, [FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader]: FALSE, - [FeatureFlag.Pm3478RefactorOrganizationUserApi]: FALSE, [FeatureFlag.AccessIntelligence]: FALSE, [FeatureFlag.Pm13322AddPolicyDefinitions]: FALSE, [FeatureFlag.LimitCollectionCreationDeletionSplit]: FALSE, From e1dce75f56a7ffa0bcddc05656ebfc17138e2f8c Mon Sep 17 00:00:00 2001 From: Julien Mailleret <8582351+jmlrt@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:39:09 +0200 Subject: [PATCH 075/126] Allow cli snap to access home and removable-media (#5577) This commit is adding back the home interface to the cli snap package. This is needed to allow saving Bitwarden attachments and exports to the disk. Also, add removable-media permission to allow saving Bitwarden attachments and exports to USB disks and equivalents. The home interface was previously present but has been removed as part of https://github.com/bitwarden/clients/pull/3596. This commit should fix https://github.com/bitwarden/clients/issues/3923 Co-authored-by: Bernd Schoolmann --- apps/cli/stores/snap/snapcraft.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/cli/stores/snap/snapcraft.yaml b/apps/cli/stores/snap/snapcraft.yaml index adb35a66b1a..df4105cb8db 100644 --- a/apps/cli/stores/snap/snapcraft.yaml +++ b/apps/cli/stores/snap/snapcraft.yaml @@ -35,7 +35,8 @@ apps: command: bw environment: XDG_CONFIG_HOME: $SNAP_USER_DATA - plugs: [network, network-bind, desktop] + plugs: [network, network-bind, desktop, home, removable-media] + parts: bw: plugin: dump From ed2415f4bda65ce291b7c54772294d622452c908 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:45:13 +0000 Subject: [PATCH 076/126] Autosync the updated translations (#11704) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 46 ++++++++++++- apps/web/src/locales/ar/messages.json | 48 +++++++++++++- apps/web/src/locales/az/messages.json | 48 +++++++++++++- apps/web/src/locales/be/messages.json | 46 ++++++++++++- apps/web/src/locales/bg/messages.json | 48 +++++++++++++- apps/web/src/locales/bn/messages.json | 46 ++++++++++++- apps/web/src/locales/bs/messages.json | 46 ++++++++++++- apps/web/src/locales/ca/messages.json | 48 +++++++++++++- apps/web/src/locales/cs/messages.json | 46 ++++++++++++- apps/web/src/locales/cy/messages.json | 46 ++++++++++++- apps/web/src/locales/da/messages.json | 46 ++++++++++++- apps/web/src/locales/de/messages.json | 54 ++++++++++++++-- apps/web/src/locales/el/messages.json | 82 ++++++++++++++++++------ apps/web/src/locales/en_GB/messages.json | 46 ++++++++++++- apps/web/src/locales/en_IN/messages.json | 46 ++++++++++++- apps/web/src/locales/eo/messages.json | 46 ++++++++++++- apps/web/src/locales/es/messages.json | 48 +++++++++++++- apps/web/src/locales/et/messages.json | 48 +++++++++++++- apps/web/src/locales/eu/messages.json | 46 ++++++++++++- apps/web/src/locales/fa/messages.json | 48 +++++++++++++- apps/web/src/locales/fi/messages.json | 50 ++++++++++++++- apps/web/src/locales/fil/messages.json | 46 ++++++++++++- apps/web/src/locales/fr/messages.json | 48 +++++++++++++- apps/web/src/locales/gl/messages.json | 46 ++++++++++++- apps/web/src/locales/he/messages.json | 46 ++++++++++++- apps/web/src/locales/hi/messages.json | 46 ++++++++++++- apps/web/src/locales/hr/messages.json | 48 +++++++++++++- apps/web/src/locales/hu/messages.json | 48 +++++++++++++- apps/web/src/locales/id/messages.json | 46 ++++++++++++- apps/web/src/locales/it/messages.json | 48 +++++++++++++- apps/web/src/locales/ja/messages.json | 48 +++++++++++++- apps/web/src/locales/ka/messages.json | 46 ++++++++++++- apps/web/src/locales/km/messages.json | 46 ++++++++++++- apps/web/src/locales/kn/messages.json | 46 ++++++++++++- apps/web/src/locales/ko/messages.json | 48 +++++++++++++- apps/web/src/locales/lv/messages.json | 50 ++++++++++++++- apps/web/src/locales/ml/messages.json | 46 ++++++++++++- apps/web/src/locales/mr/messages.json | 46 ++++++++++++- apps/web/src/locales/my/messages.json | 46 ++++++++++++- apps/web/src/locales/nb/messages.json | 48 +++++++++++++- apps/web/src/locales/ne/messages.json | 46 ++++++++++++- apps/web/src/locales/nl/messages.json | 46 ++++++++++++- apps/web/src/locales/nn/messages.json | 46 ++++++++++++- apps/web/src/locales/or/messages.json | 46 ++++++++++++- apps/web/src/locales/pl/messages.json | 48 +++++++++++++- apps/web/src/locales/pt_BR/messages.json | 48 +++++++++++++- apps/web/src/locales/pt_PT/messages.json | 56 ++++++++++++++-- apps/web/src/locales/ro/messages.json | 46 ++++++++++++- apps/web/src/locales/ru/messages.json | 48 +++++++++++++- apps/web/src/locales/si/messages.json | 46 ++++++++++++- apps/web/src/locales/sk/messages.json | 48 +++++++++++++- apps/web/src/locales/sl/messages.json | 46 ++++++++++++- apps/web/src/locales/sr/messages.json | 48 +++++++++++++- apps/web/src/locales/sr_CS/messages.json | 46 ++++++++++++- apps/web/src/locales/sv/messages.json | 48 +++++++++++++- apps/web/src/locales/te/messages.json | 46 ++++++++++++- apps/web/src/locales/th/messages.json | 46 ++++++++++++- apps/web/src/locales/tr/messages.json | 64 +++++++++++++++--- apps/web/src/locales/uk/messages.json | 48 +++++++++++++- apps/web/src/locales/vi/messages.json | 48 +++++++++++++- apps/web/src/locales/zh_CN/messages.json | 52 +++++++++++++-- apps/web/src/locales/zh_TW/messages.json | 48 +++++++++++++- 62 files changed, 2856 insertions(+), 128 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 46ca8e7736a..2a3e2a91a98 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Genereer Wagwoord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Gaan na of wagwoord blootgestel is." }, @@ -663,6 +666,10 @@ "message": "Kopieer wagwoord", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Skep rekening" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Teken aan" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Genereer gebruikersnaam" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Gebruikersnaamtipe" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Genereer ’n e-posalias met ’n eksterne aanstuurdiens." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index b1dc5093e95..7885dc39d0f 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "توليد كلمة مرور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "تحقق مما إذا تم الكشف عن كلمة المرور." }, @@ -663,6 +666,10 @@ "message": "نسخ كلمة المرور", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "استخدم طريقة أخرى للولوج" }, - "loginWithPasskey": { - "message": "تسجيل الدخول باستخدام مفتاح المرور" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "مفتاح المرور غير صالح. الرجاء المحاولة مرة أخرى." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "إنشاء حساب" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "تسجيل الدخول" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index b312f48280a..c937d724d9d 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Parol yarat" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Parolun ifşalanıb ifşalanmadığını yoxlayın." }, @@ -663,6 +666,10 @@ "message": "Parolu kopyala", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Parol kopyalandı" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Fərqli bir giriş üsulu istifadə edin" }, - "loginWithPasskey": { - "message": "Parolla giriş et" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Yararsız keçid açarı. Lütfən yenidən sınayın." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Hesab yarat" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Güclü bir parol təyin et" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Giriş et" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Kimliyinizi doğrulayın" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "İstifadəçi adı yarat" }, + "generateEmail": { + "message": "E-poçt yarat" + }, "usernameType": { "message": "İstifadəçi adı növü" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Xarici yönləndirmə xidməti ilə e-poçt ləqəbi yaradın." }, + "forwarderDomainName": { + "message": "E-poçt domeni", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçilmiş xidmət tərəfindən dəstəklənən bir domen seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ xətası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL-si" }, + "selfHostBaseUrl": { + "message": "Self-host server URL-si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Domen ləqəbi" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Bu qoşmanı birdəfəlik silmək istədiyinizə əminsiniz?" + }, + "manageSubscriptionFromThe": { + "message": "Abunəliyi buradan idarə et:", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Bitwarden-i öz serverinizdə quraşdırmaq üçün lisenziya faylınızı yükləməlisiniz. Self-hosted təşkilatınız üçün Ödənişsiz Ailələr planlarını və qabaqcıl faktura imkanlarını dəstəkləmək üçün, self-hosted təşkilatınızda avto-sinxronlaşdırmanı qurmalısınız." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index bb4dfa1a313..45371bbf914 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Генерыраваць пароль" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Праверце, ці не скампраметаваны пароль." }, @@ -663,6 +666,10 @@ "message": "Скапіяваць пароль", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Стварыць уліковы запіс" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Увайсці" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Генерыраваць імя карыстальніка" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тып імя карыстальніка" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Генерыраваць псеўданім электроннай пошты са знешнім сэрвісам перасылкі." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Адрас сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Мянушка дамена" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index b7d7413467a..98415770179 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Нова парола" }, + "generatePassphrase": { + "message": "Генериране на парола-фраза" + }, "checkPassword": { "message": "Проверка дали паролата е разкрита." }, @@ -663,6 +666,10 @@ "message": "Копиране на паролата", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Копиране на паролата-фраза", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Паролата е копирана" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Използване на друг метод на вписване" }, - "loginWithPasskey": { - "message": "Вписване със секретен ключ" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Грешен секретен ключ. Опитайте отново." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Създаване на абонамент" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Използвайте сложна парола" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Вписване" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Потвърдете самоличността си" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Генериране на потр. име" }, + "generateEmail": { + "message": "Генериране на електронна поща" + }, "usernameType": { "message": "Тип потребителско име" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Домейн на електронната поща", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Изберете домейн, който се поддържа от избраната услуга", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Грешка от $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Адрес на сървъра" }, + "selfHostBaseUrl": { + "message": "Адрес на собствения сървър", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Псевдонимен домейн" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Наистина ли искате да изтриете завинаги този прикачен файл?" + }, + "manageSubscriptionFromThe": { + "message": "Управлявайте абонамента от тук:", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "За да използвате Битуорден на собствен сървър, трябва да качите своя файл с лиценз. Ако искате да поддържате безплатни Семейни планове и разширени опции за таксуване в своята организация със собствен хостинг, ще трябва да настроите автоматичното синхронизиране в организацията си." + }, + "selfHostingTitleProper": { + "message": "Собствен хостинг" } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 267eaf7056d..f7a3415efe9 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "পাসওয়ার্ড তৈরি করুন" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "পাসওয়ার্ড অনুলিপিত করুন", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "অ্যাকাউন্ট তৈরি" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 6df1bd208ed..45813699704 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Kopirajte Lozinku", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index caf0b813a21..613a47ce057 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Genera contrasenya" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Comprova si la contrasenya ha estat exposada." }, @@ -663,6 +666,10 @@ "message": "Copia la contrasenya", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Utilitzeu un mètode d'inici de sessió diferent" }, - "loginWithPasskey": { - "message": "Inicieu sessió amb la clau de pas" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Clau d'accés no vàlida. Torneu-ho a provar." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Crea un compte" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Estableix una contrasenya segura" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Inicia sessió" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verificació de la vostra identitat" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Genera un nom d'usuari" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipus de nom d'usuari" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Genera un àlies de correu electrònic amb un servei de reenviament extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alies de domini" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 0f523094b40..d6bb5e45d1b 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Vygenerovat heslo" }, + "generatePassphrase": { + "message": "Vygenerovat heslovou frázi" + }, "checkPassword": { "message": "Zkontrolujte, zda nedošlo k úniku hesla." }, @@ -663,6 +666,10 @@ "message": "Kopírovat heslo", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Kopírovat heslovou frázi", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Heslo bylo zkopírováno" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Použít jinou metodu přihlášení" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Přihlásit se pomocí přístupového klíče" }, + "useSingleSignOn": { + "message": "Použít jednotné přihlášení" + }, + "welcomeBack": { + "message": "Vítejte zpět" + }, "invalidPasskeyPleaseTryAgain": { "message": "Neplatný přístupový klíč. Zkuste to znovu." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Vytvořit účet" }, + "newToBitwarden": { + "message": "Jste noví na Bitwardenu?" + }, "setAStrongPassword": { "message": "Nastavit hlavní heslo" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Přihlásit se" }, + "logInToBitwarden": { + "message": "Přihlásit se do Bitwardenu" + }, "verifyIdentity": { "message": "Ověřte svou totožnost" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Vygenerovat uživatelské jméno" }, + "generateEmail": { + "message": "Vygenerovat e-mail" + }, "usernameType": { "message": "Typ uživatelského jména" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Vygeneruje alias e-mailu pomocí externí přeposílací služby." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, která je podporována vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Chyba $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL serveru" }, + "selfHostBaseUrl": { + "message": "Adresa URL serveru vlastního hostování", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Doména aliasu" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Opravdu chcete tuto přílohu navždy smazat?" + }, + "manageSubscriptionFromThe": { + "message": "Spravovat předplatné z", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Chcete-li hostovat Bitwarden na vlastním serveru, musíte nahrát licenční soubor. Chcete-li podporovat plány Free Families a pokročilé možnosti účtování pro Vaši vlastní hostovanou organizaci, budete muset nastavit automatickou synchronizaci ve Vaší vlastní hostované organizaci." + }, + "selfHostingTitleProper": { + "message": "Vlastní hosting" } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 6096453f327..b61475b511f 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index c7fd97a0503..b2edfe31c13 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generér adgangskode" }, + "generatePassphrase": { + "message": "Generér adgangssætning" + }, "checkPassword": { "message": "Tjek, om adgangskode er blevet kompromitteret." }, @@ -663,6 +666,10 @@ "message": "Kopiér adgangskode", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Kopiér adgangssætning", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Adgangskode kopieret" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Benyt en anden login-metode" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log ind med adgangsnøgle" }, + "useSingleSignOn": { + "message": "Brug Single Sign-On" + }, + "welcomeBack": { + "message": "Velkommen tilbage" + }, "invalidPasskeyPleaseTryAgain": { "message": "Ugyldig adgangsnøgle. Prøv igen." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Opret konto" }, + "newToBitwarden": { + "message": "Ny på Bitwarden?" + }, "setAStrongPassword": { "message": "Indstil en stærk adgangskode" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log ind" }, + "logInToBitwarden": { + "message": "Log ind på Bitwarden" + }, "verifyIdentity": { "message": "Bekræft din identitet" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generér brugernavn" }, + "generateEmail": { + "message": "Generér e-mail" + }, "usernameType": { "message": "Brugernavnstype" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generér et e-mail alias med en ekstern viderestillingstjeneste." }, + "forwarderDomainName": { + "message": "E-maildomæne", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vælg et domæne understøttet af den valgte tjeneste", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fejl: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "URL til selv-hostet server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Aliasdomæne" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Sikker på, at denne vedhæftning skal slettes permanent?" + }, + "manageSubscriptionFromThe": { + "message": "Håndtér abonnement fra", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "For hoste Bitwarden på egen server, skal man uploade sin licensfil. Opsæt automatisk faktureringssynk for den selv-hostede organisation for at understøtte Free Families-abonnementstyper og avancerede faktureringsmuligheder for organisationen." + }, + "selfHostingTitleProper": { + "message": "Selv-hosting" } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 8badf3d866b..e75f80a4f5d 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Passwort generieren" }, + "generatePassphrase": { + "message": "Passphrase generieren" + }, "checkPassword": { "message": "Überprüfen ob ihr Kennwort kompromittiert ist." }, @@ -663,6 +666,10 @@ "message": "Passwort kopieren", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Passphrase kopieren", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Passwort kopiert" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Eine andere Anmeldemethode verwenden" }, - "loginWithPasskey": { - "message": "Mit Passkey anmelden" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Ungültiger Passkey. Bitte versuche es erneut." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Konto erstellen" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Lege ein starkes Passwort fest" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Anmelden" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verifiziere deine Identität" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Benutzername generieren" }, + "generateEmail": { + "message": "E-Mail generieren" + }, "usernameType": { "message": "Benutzernamenstyp" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generiere ein E-Mail-Alias mit einem externen Weiterleitungsdienst." }, + "forwarderDomainName": { + "message": "E-Mail-Domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Wähle eine Domain aus, die vom ausgewählten Dienst unterstützt wird", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ Fehler: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8208,10 +8238,10 @@ "message": "Sammlungserstellung und -löschung auf Eigentümer und Administratoren beschränken" }, "limitCollectionCreationDesc": { - "message": "Begrenze die Erstellung von Sammlungen auf Besitzer und Administratoren" + "message": "Das Erstellen von Sammlungen auf Eigentümer und Administratoren beschränken" }, "limitCollectionDeletionDesc": { - "message": "Begrenze das Löschen von Sammlungen auf Besitzer und Administratoren" + "message": "Das Löschen von Sammlungen auf Eigentümer und Administratoren beschränken" }, "allowAdminAccessToAllCollectionItemsDesc": { "message": "Besitzer und Administratoren können alle Sammlungen und Einträge verwalten" @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "Selbst gehostete Server-URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias-Domain" }, @@ -9411,7 +9445,7 @@ "message": "Verwende ein verknüpftes Feld, wenn du Probleme mit dem automatischen Ausfüllen einer bestimmten Website hast." }, "linkedLabelHelpText": { - "message": "Gib die HTML ID des Felds, Name, Aria-Label oder einen Platzhalter ein." + "message": "Gib die HTML-ID des Felds, Name, Aria-Label oder einen Platzhalter ein." }, "addAttachment": { "message": "Anhang hinzufügen" @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Bist du sicher, dass du diesen Anhang dauerhaft löschen möchtest?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Um Bitwarden auf deinem eigenen Server zu hosten, must du deine Lizenzdatei hochladen. Um Free Families-Tarife und erweiterte Abrechnungsmöglichkeiten für deine selbst gehostete Organisation anzubieten, musst du die automatische Synchronisierung für deine selbst gehostete Organisation einrichten." + }, + "selfHostingTitleProper": { + "message": "Selbst gehostet" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 3813c463273..17462f8a0f6 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -1,9 +1,9 @@ { "allApplications": { - "message": "All applications" + "message": "Όλες οι εφαρμογές" }, "priorityApplications": { - "message": "Priority applications" + "message": "Εφαρμογές προτεραιότητας" }, "accessIntelligence": { "message": "Access Intelligence" @@ -12,7 +12,7 @@ "message": "Notified members" }, "allApplicationsWithCount": { - "message": "All applications ($COUNT$)", + "message": "Όλες οι εφαρμογές ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -21,7 +21,7 @@ } }, "priorityApplicationsWithCount": { - "message": "Priority applications ($COUNT$)", + "message": "Εφαρμογές προτεραιότητας ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -39,25 +39,25 @@ } }, "application": { - "message": "Application" + "message": "Εφαρμογή" }, "atRiskPasswords": { "message": "At-risk passwords" }, "totalPasswords": { - "message": "Total passwords" + "message": "Σύνολο κωδικών πρόσβασης" }, "atRiskMembers": { "message": "At-risk members" }, "totalMembers": { - "message": "Total members" + "message": "Σύνολο μελών" }, "atRiskApplications": { "message": "At-risk applications" }, "totalApplications": { - "message": "Total applications" + "message": "Σύνολο εφαρμογών" }, "whatTypeOfItem": { "message": "Τι είδους στοιχείο είναι αυτό;" @@ -405,6 +405,9 @@ "generatePassword": { "message": "Δημιουργία Κωδικού" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Ελέγξτε εάν ο κωδικός έχει εκτεθεί." }, @@ -663,8 +666,12 @@ "message": "Αντιγραφή κωδικού πρόσβασης", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { - "message": "Password copied" + "message": "Ο κωδικός αντιγράφηκε" }, "copyUsername": { "message": "Αντιγραφή ονόματος χρήστη", @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Χρήση διαφορετικής μεθόδου σύνδεσης" }, - "loginWithPasskey": { - "message": "Σύνδεση με κλειδί πρόσβασης" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Μη έγκυρο κλειδί πρόσβασης. Παρακαλώ προσπαθήστε ξανά." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Δημιουργία Λογαριασμού" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Ορίστε έναν ισχυρό κωδικό πρόσβασης" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Είσοδος" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Επαληθεύστε την ταυτότητά σας" }, @@ -5625,7 +5644,7 @@ "message": "Ο Κύριος κωδικός πρόσβασης δεν πληροί τις απαιτήσεις πολιτικής αυτού του οργανισμού. Για να γίνετε μέλος του οργανισμού, πρέπει να ενημερώσετε το Κύριο Συνθηματικό σας τώρα. Η διαδικασία θα σας αποσυνδέσει από την τρέχουσα συνεδρία σας, απαιτώντας από εσάς να συνδεθείτε ξανά. Οι ενεργές συνεδρίες σε άλλες συσκευές ενδέχεται να συνεχίσουν να είναι ενεργές για μία ώρα." }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "Ο κύριος κωδικός πρόσβασής σας δεν πληροί μία ή περισσότερες πολιτικές του οργανισμού σας. Για να αποκτήσετε πρόσβαση στην κρύπτη, πρέπει να ενημερώσετε τον κύριο κωδικό πρόσβασής σας τώρα. Η διαδικασία αυτή θα σας αποσυνδέσει από την τρέχουσα συνεδρία σας, απαιτώντας από εσάς να συνδεθείτε ξανά. Οι ενεργές συνεδρίες σε άλλες συσκευές ενδέχεται να παραμείνουν ενεργές για μία ώρα." }, "automaticAppLogin": { "message": "Automatically log in users for allowed applications" @@ -5674,7 +5693,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "$HOURS$ ώρα(ες) και $MINUTES$ λεπτό(ά) το πολύ.", "placeholders": { "hours": { "content": "$1", @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Δημιουργία Ονόματος Χρήστη" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Τύπος Ονόματος Χρήστη" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Δημιουργήστε ένα ψευδώνυμο διεύθυνσης ηλ. ταχυδρομείου με μια εξωτερική υπηρεσία προώθησης." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ σφάλμα: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -7594,7 +7624,7 @@ "message": "Unlock with biometrics" }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "Ξεκλείδωμα με PIN" }, "unlockWithMasterPassword": { "message": "Unlock with master password" @@ -7864,7 +7894,7 @@ "message": "Require existing members to change their passwords" }, "smProjectDeleteAccessRestricted": { - "message": "You don't have permissions to delete this project", + "message": "Δεν έχετε δικαιώματα για να διαγράψετε αυτό το έργο", "description": "The individual description shown to the user when the user doesn't have access to delete a project." }, "smProjectsDeleteBulkConfirmation": { @@ -7914,7 +7944,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkTwo": { - "message": "SSO required", + "message": "Απαιτείται SSO", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartThree": { @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL διακομιστή" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Ψευδώνυμο τομέα" }, @@ -8320,7 +8354,7 @@ "message": "Collection access is restricted" }, "readOnlyCollectionAccess": { - "message": "You do not have access to manage this collection." + "message": "Δεν έχετε πρόσβαση για να διαχειριστείτε αυτήν τη συλλογή." }, "grantAddAccessCollectionWarningTitle": { "message": "Missing Can Manage Permissions" @@ -8721,7 +8755,7 @@ "message": "Machine account access updated" }, "restrictedGroupAccessDesc": { - "message": "You cannot add yourself to a group." + "message": "Δεν μπορείτε να προσθέσετε τον εαυτό σας σε μια ομάδα." }, "deleteProvider": { "message": "Διαγραφή παρόχου" @@ -9142,7 +9176,7 @@ "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." }, "personalItemsTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ στοιχεία θα μεταφερθούν οριστικά στον επιλεγμένο οργανισμό. Δεν θα κατέχετε πλέον αυτά τα στοιχεία.", "placeholders": { "personal_items_count": { "content": "$1", @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Θέλετε σίγουρα να διαγράψετε οριστικά αυτό το συνημμένο;" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 942d579ec8c..a1675585b65 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your licence file. To support Free Families plans and advanced billing capabilities for your self-hosted organisation, you will need to set up automatic sync in your self-hosted organisation." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index bcfbcaa53aa..02dad7b40b6 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your licence file. To support Free Families plans and advanced billing capabilities for your self-hosted organisation, you will need to set up automatic sync in your self-hosted organisation." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 0489c9d55d5..6984e4e90e6 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generi pasvorton" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Kontrolu ĉu pasvorto estis elmontrita." }, @@ -663,6 +666,10 @@ "message": "Kopii pasvorton", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Krei konton" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Saluti" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index ed6e8683d79..0596f251768 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generar contraseña" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Comprobar si la contraseña está comprometida." }, @@ -663,6 +666,10 @@ "message": "Copiar contraseña", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Utilizar otro método de inicio de sesión" }, - "loginWithPasskey": { - "message": "Iniciar sesión con la clave de acceso" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Clave inválida. Por favor, inténtelo de nuevo." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Crear cuenta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Establece una contraseña fuerte" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Identificarse" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verifica tu identidad" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generar nombre de usuario" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de nombre de usuario" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generar un alias de correo electrónico mediante un servicio de reenvío externo." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Dominio alias" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index e748ff94a71..0eac8f3be63 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Loo parool" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Kontrolli, kas parool on lekkinud." }, @@ -663,6 +666,10 @@ "message": "Kopeeri parool", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Kasuta teist logimismeetodit" }, - "loginWithPasskey": { - "message": "Logi sisse pääsuvõtmega" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Vigane pääsuvõti. Palun proovi uuesti." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Konto loomine" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Määra tugev parool" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Logi sisse" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Kinnitage oma Identiteet" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Genereeri kasutajanimi" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Kasutajanime tüüp" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Genereeri e-posti alias, kasutades selleks välist teenuspakkujat." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Serveri URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domeen" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 613b534a80e..9d58aa1c79c 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Sortu pasahitza" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Egiaztatu pasahitza konprometituta dagoen." }, @@ -663,6 +666,10 @@ "message": "Kopiatu pasahitza", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Sortu kontua" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Hasi saioa" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Sortu erabiltzaile izena" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Erabiltzaile izen mota" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Emaileko ezizen bat sortu kanpoko bidalketa zerbitzu batekin." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index befc0c1af56..f8f904c4f8b 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "تولید کلمه عبور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "بررسی کنید که آیا کلمه عبور افشا شده است." }, @@ -663,6 +666,10 @@ "message": "کپی کلمه عبور", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { - "message": "با کلید عبور وارد شوید" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "ایجاد حساب کاربری" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "ورود" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "ایجاد نام کاربری" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع نام کاربری" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "یک نام مستعار ایمیل با یک سرویس ارسال خارجی ایجاد کنید." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "نشانی اینترنتی سرور" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "دامنه مستعار" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 8082075fa0c..76b2e75c02a 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Luo salasana" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Tarkasta, onko salasana paljastunut." }, @@ -663,8 +666,12 @@ "message": "Kopioi salasana", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { - "message": "Password copied" + "message": "Salasana kopioitu" }, "copyUsername": { "message": "Kopioi käyttäjätunnus", @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Käytä vaihtoehtoista kirjautumistapaa" }, - "loginWithPasskey": { - "message": "Kirjaudu pääsyavaimella" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Pääsyavain ei kelpaa. Yritä uudelleen." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Luo uusi tili" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Aseta vahva salasana" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Kirjaudu sisään" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Vahvista henkilöllisyytesi" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Luo käyttäjätunnus" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Käyttäjätunnuksen tyyppi" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Luo sähköpostialias ulkoisella ohjauspalvelulla." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ -virhe: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Palvelimen URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Aliaksen verkkotunnus" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Haluatko varmasti poistaa liitteen pysyvästi?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 7ccd0863180..17cf9b683df 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Gumawa ng password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Tingnan kung nakompromiso na ba ang password." }, @@ -663,6 +666,10 @@ "message": "Kopyahin ang password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Gumawa ng account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Mag-log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Lumikha ng username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Uri ng username" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Bumuo ng isang email alias na may isang panlabas na serbisyo sa pagpapasa." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index d952fb454c5..38890b3c1fb 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Générer un mot de passe" }, + "generatePassphrase": { + "message": "Générer une phrase de passe" + }, "checkPassword": { "message": "Vérifier si le mot de passe a été exposé." }, @@ -663,6 +666,10 @@ "message": "Copier le mot de passe", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copier la phrase de passe", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Mot de passe copié" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Utiliser une méthode de connexion différente" }, - "loginWithPasskey": { - "message": "Se connecter avec une clé d'accès" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Passkey invalide. Veuillez réessayer de nouveau." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Créez un compte" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Définir un mot de passe fort" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Se connecter" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Vérifiez votre Identité" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Générer le Nom d'Utilisateur" }, + "generateEmail": { + "message": "Générer un courriel" + }, "usernameType": { "message": "Type de Nom d'Utilisateur" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Générer un alias de courriel avec un service de transfert externe." }, + "forwarderDomainName": { + "message": "Domaine du courriel", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choisissez un domaine qui est supporté par le service sélectionné", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erreur $SERVICENAME$ : $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL du serveur" }, + "selfHostBaseUrl": { + "message": "URL du serveur auto-hébergé", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Domaine de l'alias" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Êtes-vous sûr de vouloir supprimer définitivement cette pièce jointe ?" + }, + "manageSubscriptionFromThe": { + "message": "Gérer l'abonnement à partir du", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Pour héberger Bitwarden sur votre propre serveur, vous devrez téléverser votre fichier de licence. Pour prendre en charge les forfaits Familles Gratuits et les fonctionnalités de facturation avancées pour votre organisation auto-hébergée, vous devrez configurer la synchronisation automatique dans votre organisation auto-hébergée." + }, + "selfHostingTitleProper": { + "message": "Auto-Hébergement" } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 77e6f6190ec..8559723bf8d 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index d95eb253250..c4857d603f7 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "צור סיסמה" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "בדוק אם הסיסמה נחשפה." }, @@ -663,6 +666,10 @@ "message": "העתק סיסמה", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "צור חשבון" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "התחבר" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 3ff096bd8b5..3b6800c26cd 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "खाता बनाएँ" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index d6454bdfe10..3d10e36712a 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generiraj lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Provjeri je li lozinka bila ukradena." }, @@ -663,6 +666,10 @@ "message": "Kopiraj lozinku", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Koristi drugi način prijave" }, - "loginWithPasskey": { - "message": "Prijava pristupnim ključem" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Nevažeći pristupni ključ. Pokušaj ponovno." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Stvori račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Postavi jaku lozinku" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Prijavi se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Potvrdi svoj identitet" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generiraj korisničko ime" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip korisničkog imena" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ greška: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL poslužitelja" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domene" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 04d0a4e9140..2c5f9daabbc 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Jelszó generálása" }, + "generatePassphrase": { + "message": "Jelmondat generálás" + }, "checkPassword": { "message": "A jelszóvédelmi állapot ellenőrzése." }, @@ -663,6 +666,10 @@ "message": "Jelszó másolása", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Jelmondat másolása", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "A jelszó másolásra került." }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Más bejelentkezési mód használata" }, - "loginWithPasskey": { - "message": "Bejelentkezés jelszóval" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Érvénytelen jelszó. Próbálja újra." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Fiók létrehozása" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Erős jelszó beállítása" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Bejelentkezés" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Személyazonosság ellenőrzése" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Felhasználónév generálása" }, + "generateEmail": { + "message": "Email generálása" + }, "usernameType": { "message": "Felhasználónév típusa" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Email álnév generálása külső továbbító szolgáltatással." }, + "forwarderDomainName": { + "message": "Email tartomány", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Válasszunk a kiválasztott szolgáltatás által támogatott tartományt.", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hiba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Szerver webcím" }, + "selfHostBaseUrl": { + "message": "Saját üzemeltetésű szerver webcím", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Áldomain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Biztosan véglegesen törlésre kerüljön ez a melléklet?" + }, + "manageSubscriptionFromThe": { + "message": "Előfizetés kezelése:", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Ha a Bitwarden szolgáltatást saját szerveren szeretnénk tárolni, fel kell tölteni a licenszfájlt. A Free Families csomagok és a speciális számlázási lehetőségek támogatásához a saját üzemeltetésű szervezet számára be kell állítani az automatikus szinkronizálást a saját üzemeltetésű szervezetben." + }, + "selfHostingTitleProper": { + "message": "Sahát üzemeltetés" } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index d5e966ccdf8..f13892c4092 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Buat Kata Sandi" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Periksa apakah kata sandi telah terekspos." }, @@ -663,6 +666,10 @@ "message": "Salin Kata Sandi", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Buat Akun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Masuk" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Jenis nama pengguna" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index dabdc2cd755..8f9707cdfde 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Genera password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Verifica se la password è stata esposta." }, @@ -663,6 +666,10 @@ "message": "Copia password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Usa un altro metodo di accesso" }, - "loginWithPasskey": { - "message": "Accedi con passkey" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Passkey non valida. Riprova." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Crea account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Imposta una password robusta" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Accedi" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verifica la tua identità" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Genera nome utente" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo di nome utente" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Genera un alias email con un servizio di inoltro esterno." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Errore $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL del server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Dominio alias" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index e755269f24d..465405f8c94 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "パスワードの自動生成" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "パスワードが漏洩していないか確認する" }, @@ -663,6 +666,10 @@ "message": "パスワードのコピー", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "別のログイン方法を使用する" }, - "loginWithPasskey": { - "message": "パスキー でログイン" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "無効なパスキーです。もう一度やり直してください。" @@ -1016,6 +1029,9 @@ "createAccount": { "message": "アカウントの作成" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "強力なパスワードを設定する" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "ログイン" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "本人確認" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "ユーザー名を生成" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "ユーザー名の種類" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "外部転送サービスを使用してメールエイリアスを生成します。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ エラー: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "サーバー URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "エイリアスドメイン" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "この添付ファイルを完全に削除してもよろしいですか?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index b4a109834d2..0ca4ffa0c54 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "წარმოქმენი პაროლი" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "შეამოწმე პაროლი თუ გაბაზრდა" }, @@ -663,6 +666,10 @@ "message": "ასლირება პაროლის", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "ანგარიშის შექმნა" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "ავტორიზაცია" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 8419c2fcd41..d73481686f6 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 448970dec7a..1459f7f7693 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "ಪಾಸ್ವರ್ಡ್ ಬಹಿರಂಗಗೊಂಡಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಿ." }, @@ -663,6 +666,10 @@ "message": "ಪಾಸ್ವರ್ಡ್ ನಕಲಿಸಿ", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "ಖಾತೆ ತೆರೆ" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "ಲಾಗಿನ್" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 03c9c13ddcc..24bec17f849 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "비밀번호 생성" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "비밀번호가 노출되었는지 확인합니다." }, @@ -663,6 +666,10 @@ "message": "비밀번호 복사", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { - "message": "패스키로 로그인" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "계정 만들기" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "로그인" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index ce0ffc3fde3..7d614ff867a 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Veidot paroli" }, + "generatePassphrase": { + "message": "Izveidot paroles vārdkopu" + }, "checkPassword": { "message": "Pārbaudīt, vai parole ir bijusi nopludināta." }, @@ -663,8 +666,12 @@ "message": "Ievietot paroli starpliktuvē", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Ievietot paroles vārdkopu starpliktuvē", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { - "message": "Password copied" + "message": "Parole ievietota starpliktuvē" }, "copyUsername": { "message": "Ievietot lietotājvārdu starpliktuvē", @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Jāizmanto cits pieteikšanās veids" }, - "loginWithPasskey": { - "message": "Pieteikties ar piekļuves atslēgu" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Nederīga piekļuves atslēga. Lūgums mēģināt vēlreiz." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Izveidot kontu" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Jāiestata droša parole" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Pieteikties" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Jāapliecina sava identitāte" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Izveidot lietotājvārdu" }, + "generateEmail": { + "message": "Izveidot e-pastu" + }, "usernameType": { "message": "Lietotājvārda veids" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Izveidot e-pastu aizstājvārdu ar ārēju pārvirzīšanas pakalpojumu." }, + "forwarderDomainName": { + "message": "E-pasta domēns", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Jāizvēlas domēns, kuru atbalsta atlasītais pakalpojums", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ kļūda: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Servera URL" }, + "selfHostBaseUrl": { + "message": "Pašmitināta servera URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Aizstājdomēns" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Vai tiešām neatgriezeniski izdzēst šo pielikumu?" + }, + "manageSubscriptionFromThe": { + "message": "Pārvaldīt abonementu", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Lai mitinātu Bitwarden savā serverī, ir nepieciešams augšupielādēt licences datni. Lai nodrošinātu bezmaksas ģimeņu plānus un papildu norēķinu iespējas pašmitinātajai apvienībai, tajā ir nepieciešams uzstādīt automātisku sinhronizēšanu." + }, + "selfHostingTitleProper": { + "message": "Pašmitināts" } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 43f3bd00460..bcb3ed0cbf9 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "പാസ്‌വേഡ് സൃഷ്ടിക്കുക" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "പാസ്സ്‌വേർഡ് ചോർന്നോ എന്ന് നോക്കുക." }, @@ -663,6 +666,10 @@ "message": "പാസ്‌വേഡ് ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തുക", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "അക്കൗണ്ട് സൃഷ്ടിക്കുക" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "പ്രവേശിക്കുക" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 8419c2fcd41..d73481686f6 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 8419c2fcd41..d73481686f6 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index a0542df0a0e..ce00b906165 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generer et passord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Sjekk om passordet har blitt utsatt." }, @@ -663,6 +666,10 @@ "message": "Kopier passordet", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Bruk en annen innloggingsmetode" }, - "loginWithPasskey": { - "message": "Logg inn med Passkey" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Ugyldig Passkey. Vennligst prøv igjen." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Opprett en konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Logg på" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generer brukernavn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Brukernavntype" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generer et e-postalias med en ekstern videresendingstjeneste." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index fe06947d3c5..2772148bf68 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 9a2c2592064..8e49a533808 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Wachtwoord genereren" }, + "generatePassphrase": { + "message": "Wachtwoordzin genereren" + }, "checkPassword": { "message": "Controleer of wachtwoord is gelekt." }, @@ -663,6 +666,10 @@ "message": "Wachtwoord kopiëren", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Wachtwoordzin kopiëren", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Wachtwoord gekopieerd" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Gebruik een andere loginmethode" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Inloggen met passkey" }, + "useSingleSignOn": { + "message": "Single sign-on gebruiken" + }, + "welcomeBack": { + "message": "Welkom terug" + }, "invalidPasskeyPleaseTryAgain": { "message": "Ongeldige passkey. Probeer het opnieuw." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Account aanmaken" }, + "newToBitwarden": { + "message": "Nieuw bij Bitwarden?" + }, "setAStrongPassword": { "message": "Sterk wachtwoord instellen" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Inloggen" }, + "logInToBitwarden": { + "message": "Inloggen op Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Gebruikersnaam genereren" }, + "generateEmail": { + "message": "E-mailadres genereren" + }, "usernameType": { "message": "Type gebruikersnaam" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Genereer een e-mailalias met een externe doorschakelservice." }, + "forwarderDomainName": { + "message": "E-maildomein", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Kies een domein dat wordt ondersteund door de geselecteerde dienst", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "URL zelfgehoste server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Aliasdomein" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Weet je zeker dat je deze bijlage definitief wilt verwijderen?" + }, + "manageSubscriptionFromThe": { + "message": "Abonnement beheren vanuit het", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Om Bitwarden op je eigen server te hosten, moet je je licentiebestand uploaden. Om gratis familie-abonnementen en geavanceerde factureringsmogelijkheden voor je zelfgehoste organisatie te ondersteunen, moet je automatische synchronisatie instellen in je zelfgehoste organisatie." + }, + "selfHostingTitleProper": { + "message": "Zelf hosten" } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 6c97ea7114f..9ee4df9aa3b 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Laga passord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Kopier passordet", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 8419c2fcd41..d73481686f6 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index aee04e8cae3..3e3f9c0c231 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Wygeneruj hasło" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Sprawdź, czy hasło zostało ujawnione." }, @@ -663,6 +666,10 @@ "message": "Kopiuj hasło", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Użyj innej metody logowania" }, - "loginWithPasskey": { - "message": "Zaloguj się za pomocą passkey" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Błędny passkey. Spróbuj ponownie." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Utwórz konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Ustaw silne hasło" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Zaloguj się" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Zweryfikuj swoją tożsamość" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Wygeneruj nazwę użytkownika" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Rodzaj nazwy użytkownika" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekierowania." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Błąd $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Adres URL serwera" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Domena aliasu" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 460d69f7859..c73978a248b 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Gerar Senha" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Verifique se a senha foi exposta." }, @@ -663,6 +666,10 @@ "message": "Copiar Senha", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Senha copiada" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Use um método de login diferente" }, - "loginWithPasskey": { - "message": "Iniciar sessão com a chave de acesso" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Senha inválida. Por favor, tente novamente." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Criar conta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Defina uma senha forte" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Iniciar sessão" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verifique sua identidade" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Gerar Usuário" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de Usuário" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Gere um alias de e-mail com um serviço externo de encaminhamento." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro $SERVICENAME$ : $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL do Servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias do domínio" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Tem certeza de que deseja excluir permanentemente esse anexo?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index f3c773fb2a7..02b7b9254b1 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Gerar palavra-passe" }, + "generatePassphrase": { + "message": "Gerar frase de acesso" + }, "checkPassword": { "message": "Verificar se a palavra-passe foi exposta." }, @@ -663,6 +666,10 @@ "message": "Copiar palavra-passe", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copiar frase de acesso", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Palavra-passe copiada" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Utilizar um método de início de sessão diferente" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Iniciar sessão com a chave de acesso" }, + "useSingleSignOn": { + "message": "Utilizar início de sessão único" + }, + "welcomeBack": { + "message": "Bem-vindo de volta" + }, "invalidPasskeyPleaseTryAgain": { "message": "Chave de acesso inválida. Por favor, tente novamente." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Criar conta" }, + "newToBitwarden": { + "message": "Novo no Bitwarden?" + }, "setAStrongPassword": { "message": "Defina uma palavra-passe forte" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Iniciar sessão" }, + "logInToBitwarden": { + "message": "Iniciar sessão no Bitwarden" + }, "verifyIdentity": { "message": "Verifique a sua identidade" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Gerar nome de utilizador" }, + "generateEmail": { + "message": "Gerar e-mail" + }, "usernameType": { "message": "Tipo de nome de utilizador" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Gerar um alias de e-mail com um serviço de reencaminhamento externo." }, + "forwarderDomainName": { + "message": "Domínio de e-mail", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Escolha um domínio que seja suportado pelo serviço selecionado", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro no $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL do servidor" }, + "selfHostBaseUrl": { + "message": "URL do servidor auto-hospedado", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias de domínio" }, @@ -9399,19 +9433,19 @@ "message": "Editar acesso" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Utilize campos de texto para dados como perguntas de segurança" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Utilize campos ocultos para dados sensíveis como uma palavra-passe" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Utilize caixas de verificação se pretender preencher automaticamente uma caixa de verificação de um formulário, como um e-mail de memorização" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Utilize um campo ligado quando tiver problemas de preenchimento automático para um site específico." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Introduza o ID do HTML, o nome, a aria-label ou o placeholder do campo." }, "addAttachment": { "message": "Adicionar anexo" @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Tem a certeza de que pretende eliminar permanentemente este anexo?" + }, + "manageSubscriptionFromThe": { + "message": "Gerir a subscrição a partir do", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Para hospedar o Bitwarden no seu próprio servidor, precisará de carregar o seu ficheiro de licença. Para suportar planos Familiar gratuitos e recursos avançados de faturamento para a sua organização auto-hospedada, precisará de configurar a sincronização automática na sua organização auto-hospedada." + }, + "selfHostingTitleProper": { + "message": "Auto-hospedagem" } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 742ade6e5a5..5d4d9846930 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generare parolă" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Verificați dacă parola a fost dezvăluită." }, @@ -663,6 +666,10 @@ "message": "Copiere parolă", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Creare cont" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Conectare" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generare nume de utilizator" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip de nume de utilizator" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generați un alias de e-mail cu un serviciu de redirecționare extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 1629ac79481..1cfc73958b9 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Сгенерировать пароль" }, + "generatePassphrase": { + "message": "Создать парольную фразу" + }, "checkPassword": { "message": "Проверьте, не скомпрометирован ли пароль." }, @@ -663,6 +666,10 @@ "message": "Скопировать пароль", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Скопировать парольную фразу", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Пароль скопирован" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Использовать другой способ авторизации" }, - "loginWithPasskey": { - "message": "Войти с passkey" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Неверный passkey. Попробуйте снова." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Создать аккаунт" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Задайте надежный пароль" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Войти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Подтвердите вашу личность" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Создать имя пользователя" }, + "generateEmail": { + "message": "Сгенерировать email" + }, "usernameType": { "message": "Тип имени пользователя" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Создать псевдоним электронной почты для внешней службы пересылки." }, + "forwarderDomainName": { + "message": "Домен электронной почты", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Выберите домен, который поддерживается выбранным сервисом", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Ошибка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL сервера" }, + "selfHostBaseUrl": { + "message": "URL собственного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Псевдоним домена" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Вы уверены, что хотите навсегда удалить это вложение?" + }, + "manageSubscriptionFromThe": { + "message": "Управление подпиской с", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Чтобы разместить Bitwarden на собственном сервере, вам нужно загрузить файл лицензии. Для поддержки тарифных планов Free Families и расширенных возможностей выставления счетов для вашей организации, размещенной на собственном сервере вам необходимо настроить автоматическую синхронизацию в вашей организации." + }, + "selfHostingTitleProper": { + "message": "Собственное размещение" } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 7e47aee0ac5..d7b4d263f6b 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "ගිණුමක් සාදන්න" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "පිවිසෙන්න" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 9a560ea908e..ea198f5d8f4 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generovať heslo" }, + "generatePassphrase": { + "message": "Generovať prístupovú frázu" + }, "checkPassword": { "message": "Overiť či došlo k úniku hesla." }, @@ -663,6 +666,10 @@ "message": "Kopírovať heslo", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Kopírovať prístupovú frázu", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Heslo skopírované" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Použiť iný spôsob prihlásenia" }, - "loginWithPasskey": { - "message": "Prihlásiť sa s prístupovým kľúčom" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Nesprávne Passkey heslo. Prosím skúste to znovu." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Vytvoriť účet" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Nastavte silné heslo" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Prihlásiť sa" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Overte svoju totožnosť" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Vygenerovať používateľské meno" }, + "generateEmail": { + "message": "Generovať e-mail" + }, "usernameType": { "message": "Typ používateľského mena" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Vytvoriť e-mailový alias pomocou externej služby preposielania." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, ktorá je podporovaná vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL servera" }, + "selfHostBaseUrl": { + "message": "Adresa URL vlastného hostingu", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias doména" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Naozaj chcete natrvalo odstrániť túto prílohu?" + }, + "manageSubscriptionFromThe": { + "message": "Spravovať predplatné z", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Ak chcete prevádzkovať Bitwarden na vlastnom serveri, musíte nahrať licenčný súbor. Pre podporu Free Families plánu a pokročilé možnosti fakturácie pre vašu organizáciu s vlastným hostingom, budete musieť nastaviť automatickú synchronizáciu v organizácii vo vlastnom hostingu." + }, + "selfHostingTitleProper": { + "message": "Vlastný hosting" } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 53b568afb54..5e2cc77cf59 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generiraj geslo" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Preveri izpostavljenost gesla." }, @@ -663,6 +666,10 @@ "message": "Kopiraj geslo", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Ustvarite si račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Prijava" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Vrsta uporabniškega imena" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Ustvari psevdonim (alias) za elektronski naslov z uporabo zunanjega ponudnika posredovanja pošte." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index 01c3c918525..f62d9cfc11a 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Генерисање лозинке" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Проверите да ли је лозинка изложена." }, @@ -663,6 +666,10 @@ "message": "Копирај лозинку", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Користи други начин пријављивања" }, - "loginWithPasskey": { - "message": "Пријавите се са приступачним кључем" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Неважећи приступни кључ. Молим вас, покушајте поново." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Креирај налог" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Поставите јаку лозинку" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Пријавите се" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Потврдите идентитет" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Генериши име" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тип имена" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ грешка: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "УРЛ Сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Домен алијаса" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Да ли сте сигурни да желите да трајно избришете овај прилог?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 097786955ff..e31a99993a2 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generiši lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Kopiraj lozinku", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Napravi Nalog" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Prijavi Se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 0a8cd517eb0..fd3078d20f9 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generera lösenord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Kontrollera om lösenordet har avslöjats." }, @@ -663,6 +666,10 @@ "message": "Kopiera lösenord", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Använd en annan inloggningsmetod" }, - "loginWithPasskey": { - "message": "Logga in med nyckel" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Ogiltig nyckel. Försök igen." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Skapa konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Ställ in ett starkt lösenord" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Logga in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generera användarnamn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Användarnamnstyp" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generera ett e-postalias med en extern vidarebefordranstjänst." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fel: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Aliasdomän" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 8419c2fcd41..d73481686f6 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Check if password has been exposed." }, @@ -663,6 +666,10 @@ "message": "Copy password", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 3fdce3bb05b..0f8e7b59e17 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "สร้างรหัสผ่าน" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "ตรวจสอบว่ารหัสผ่านถูกเปิดเผยหรือไม่" }, @@ -663,6 +666,10 @@ "message": "คัดลอกรหัสผ่าน", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Use a different log in method" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Log in with passkey" }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "invalidPasskeyPleaseTryAgain": { "message": "Invalid Passkey. Please try again." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "สร้างบัญชีผู้ใช้" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "เข้าสู่ระบบ" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias domain" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 3a9840139e6..afb66b1edd3 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Parola oluştur" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Parolanız ele geçirilip geçirilmediğini kontrol edin." }, @@ -663,6 +666,10 @@ "message": "Parolayı kopyala", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Parola kopyalandı" }, @@ -932,9 +939,15 @@ "useADifferentLogInMethod": { "message": "Başka bir giriş yöntemi kullan" }, - "loginWithPasskey": { + "logInWithPasskey": { "message": "Geçiş anahtarıyla giriş yap" }, + "useSingleSignOn": { + "message": "Çoklu oturum açma kullan" + }, + "welcomeBack": { + "message": "Tekrar hoş geldiniz" + }, "invalidPasskeyPleaseTryAgain": { "message": "Geçersiz geçiş anahtarı. Lütfen tekrar deneyin." }, @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Hesap aç" }, + "newToBitwarden": { + "message": "Bitwarden'da yeni misiniz?" + }, "setAStrongPassword": { "message": "Güçlü bir parola belirleyin" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Giriş yap" }, + "logInToBitwarden": { + "message": "Bitwarden'a giriş yapın" + }, "verifyIdentity": { "message": "Kimliğinizi doğrulayın" }, @@ -3073,7 +3092,7 @@ "message": "İlkeler" }, "singleSignOn": { - "message": "Tek oturum açma" + "message": "Çoklu oturum açma" }, "editPolicy": { "message": "İlkeyi düzenle" @@ -4566,10 +4585,10 @@ "message": "Kuruluş tanımlayıcı" }, "ssoLogInWithOrgIdentifier": { - "message": "Kuruluşunuzun tek oturum açma portalını kullanarak giriş yapabilirsiniz. Başlamak için lütfen kuruluşunuzun SSO tanımlayıcısını girin." + "message": "Kuruluşunuzun çoklu oturum açma portalını kullanarak giriş yapabilirsiniz. Başlamak için lütfen kuruluşunuzun SSO tanımlayıcısını girin." }, "enterpriseSingleSignOn": { - "message": "Kurumsal tek oturum açma" + "message": "Kurumsal çoklu oturum açma" }, "ssoHandOff": { "message": "Şimdi bu sekmeyi kapatıp uzantı üzerinden devam edebilirsiniz." @@ -4642,16 +4661,16 @@ "message": "Sahip veya yönetici olmayan ve zaten başka bir kuruluşun üyesi olan kuruluş üyeleri kuruluşunuzdan kaldırılır." }, "requireSso": { - "message": "Tek oturum açma kimlik doğrulamasını zorunlu tut" + "message": "Çoklu oturum açma kimlik doğrulamasını zorunlu tut" }, "requireSsoPolicyDesc": { - "message": "Üyelerin kurumsal tek oturum açma (SSO) yöntemiyle oturum açmasını zorunlu kılın." + "message": "Üyelerin kurumsal çoklu oturum açma (SSO) yöntemiyle oturum açmasını zorunlu kıl." }, "prerequisite": { "message": "Önkoşul" }, "requireSsoPolicyReq": { - "message": "Bu ilkeyi etkinleştirmeden önce tek kuruluş kurumsal ilkesi etkinleştirilmelidir." + "message": "Bu ilkeyi etkinleştirmeden önce çoklu kuruluş kurumsal ilkesi etkinleştirilmelidir." }, "requireSsoPolicyReqError": { "message": "Tek kuruluş ilkesi ayarlanmamış." @@ -5842,7 +5861,7 @@ "message": "Bağlama türü" }, "idpSingleSignOnServiceUrl": { - "message": "Tek oturum açma (SSO) servis URL'si" + "message": "Çoklu oturum açma (SSO) servis URL'si" }, "idpSingleLogoutServiceUrl": { "message": "Tek çıkış (SLO) servis URL'si" @@ -5863,7 +5882,7 @@ "message": "Kimlik doğrulama isteklerini imzala" }, "ssoSettingsSaved": { - "message": "Tek oturum açma yapılandırması kaydedildi" + "message": "Çoklu oturum açma yapılandırması kaydedildi" }, "sponsoredFamilies": { "message": "Ücretsiz Bitwarden Aile" @@ -6038,7 +6057,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" }, "ssoPolicyHelpAnchor": { - "message": "require single sign-on authentication policy", + "message": "çoklu oturum açma kimlik doğrulama ilkesini zorunlu tut", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" }, "ssoPolicyHelpEnd": { @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Kullanıcı adı oluştur" }, + "generateEmail": { + "message": "E-posta oluştur" + }, "usernameType": { "message": "Kullanıcı adı türü" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Harici bir yönlendirme servisiyle e-posta maskesi oluştur." }, + "forwarderDomainName": { + "message": "E-posta alan adı", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçtiğiniz servisin desteklediği bir alan adı seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hatası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Sunucu URL'si" }, + "selfHostBaseUrl": { + "message": "Kendi kendine barındırılan sunucu URL'si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Alias alan adı" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Bu dosyayı kalıcı olarak silmek istediğinizden emin misiniz?" + }, + "manageSubscriptionFromThe": { + "message": "Aboneliğinizi yönetin:", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Kendi Kendinize Barındırma" } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 1c356722c1e..2ffa11e574f 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Генерувати пароль" }, + "generatePassphrase": { + "message": "Генерувати парольну фразу" + }, "checkPassword": { "message": "Перевірити чи пароль було викрито." }, @@ -663,6 +666,10 @@ "message": "Копіювати пароль", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Копіювати парольну фразу", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Пароль скопійовано" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Використати інший спосіб входу" }, - "loginWithPasskey": { - "message": "Увійти з ключем доступу" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Недійсний ключ доступу. Повторіть спробу." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Створити обліковий запис" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Встановіть надійний пароль" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Увійти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Підтвердьте свою особу" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Генерувати ім'я користувача" }, + "generateEmail": { + "message": "Генерувати е-пошту" + }, "usernameType": { "message": "Тип імені користувача" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Згенеруйте псевдонім е-пошти зі стороннім сервісом пересилання." }, + "forwarderDomainName": { + "message": "Домен електронної пошти", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Виберіть домен, який підтримується вибраною службою", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Помилка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "URL-адреса сервера" }, + "selfHostBaseUrl": { + "message": "URL-адреса власного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Псевдонім домену" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Ви дійсно хочете остаточно видалити це вкладення?" + }, + "manageSubscriptionFromThe": { + "message": "Керуйте передплатою на", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Щоб розмістити Bitwarden на власному сервері, вам необхідно вивантажити файл ліцензії. Для підтримки безплатних сімейних тарифних планів та розширених можливостей виставлення рахунків для організації на власному хостингу, вам необхідно буде налаштувати автоматичну синхронізацію для своєї організації на власному хостингу." + }, + "selfHostingTitleProper": { + "message": "Власне розміщення" } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 487f1d2213e..ac585a9f66a 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "Tạo mật khẩu" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "Kiểm tra xem mật khẩu có bị lộ không." }, @@ -663,6 +666,10 @@ "message": "Sao chép mật khẩu", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "Dùng phương thức đăng nhập khác" }, - "loginWithPasskey": { - "message": "Đăng nhập bằng mã khoá" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "Mã khoá không hợp lệ. Vui lòng thử lại." @@ -1016,6 +1029,9 @@ "createAccount": { "message": "Tạo tài khoản" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Đặt mật khẩu mạnh" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "Đăng nhập" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "Xác minh danh tính của bạn" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "Địa chỉ máy chủ" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "Tên miền thay thế" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Quản lý đăng ký từ", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "Để lưu trữ Bitwarden trên máy chủ của riêng bạn, bạn sẽ cần tải tệp giấy phép của mình lên. Để hỗ trợ các gói Free Families và khả năng thanh toán nâng cao cho tổ chức tự lưu trữ của bạn, bạn sẽ cần thiết lập đồng bộ hóa tự động trong tổ chức tự lưu trữ của mình." + }, + "selfHostingTitleProper": { + "message": "Tự lưu trữ" } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 13087bb6441..212053d0587 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "生成密码" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "检查密码是否已暴露。" }, @@ -663,8 +666,12 @@ "message": "复制密码", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { - "message": "Password copied" + "message": "密码已复制" }, "copyUsername": { "message": "复制用户名", @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "使用不同的登录方式" }, - "loginWithPasskey": { - "message": "使用通行密钥登录" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "通行密钥无效。请重试。" @@ -1016,6 +1029,9 @@ "createAccount": { "message": "创建账户" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "设置强密码" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "登录" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "验证您的身份" }, @@ -1200,7 +1219,7 @@ "message": "您没有查看此集合中的所有项目的权限。" }, "youDoNotHavePermissions": { - "message": "You do not have permissions to this collection" + "message": "您没有此集合的权限" }, "noCollectionsInList": { "message": "没有可列出的集合。" @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "生成用户名" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "用户名类型" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "使用外部转发服务生成一个电子邮件别名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ 错误:$ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "服务器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "别名域" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "您确定要永久删除此附件吗?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "自托管" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index ad7eba55262..f40164e597d 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -405,6 +405,9 @@ "generatePassword": { "message": "產生密碼" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "checkPassword": { "message": "檢查密碼是否已暴露。" }, @@ -663,6 +666,10 @@ "message": "複製密碼", "description": "Copy password to clipboard" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "passwordCopied": { "message": "Password copied" }, @@ -932,8 +939,14 @@ "useADifferentLogInMethod": { "message": "改用不同的登入方式" }, - "loginWithPasskey": { - "message": "使用密碼金鑰登入" + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" }, "invalidPasskeyPleaseTryAgain": { "message": "無效的密碼金鑰,請再試一次。" @@ -1016,6 +1029,9 @@ "createAccount": { "message": "建立帳戶" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "設定一個強密碼" }, @@ -1031,6 +1047,9 @@ "logIn": { "message": "登入" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "verifyIdentity": { "message": "核實你的身份" }, @@ -6354,6 +6373,9 @@ "generateUsername": { "message": "產生使用者名稱" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "使用者名稱類型" }, @@ -6459,6 +6481,14 @@ "forwardedEmailDesc": { "message": "使用外部轉寄服務產生一個電子郵件別名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ 錯誤:$ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -8258,6 +8288,10 @@ "baseUrl": { "message": "伺服器網址" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "aliasDomain": { "message": "別名網域" }, @@ -9421,5 +9455,15 @@ }, "permanentlyDeleteAttachmentConfirmation": { "message": "Are you sure you want to permanently delete this attachment?" + }, + "manageSubscriptionFromThe": { + "message": "Manage subscription from the", + "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." + }, + "toHostBitwardenOnYourOwnServer": { + "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + }, + "selfHostingTitleProper": { + "message": "Self-Hosting" } } From 1a33cee56252bdaad5e3c72fe158c0222a49e761 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:46:52 +0000 Subject: [PATCH 077/126] Autosync the updated translations (#11703) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 49 +++++++++-- apps/desktop/src/locales/ar/messages.json | 49 +++++++++-- apps/desktop/src/locales/az/messages.json | 49 +++++++++-- apps/desktop/src/locales/be/messages.json | 49 +++++++++-- apps/desktop/src/locales/bg/messages.json | 49 +++++++++-- apps/desktop/src/locales/bn/messages.json | 49 +++++++++-- apps/desktop/src/locales/bs/messages.json | 49 +++++++++-- apps/desktop/src/locales/ca/messages.json | 49 +++++++++-- apps/desktop/src/locales/cs/messages.json | 49 +++++++++-- apps/desktop/src/locales/cy/messages.json | 49 +++++++++-- apps/desktop/src/locales/da/messages.json | 49 +++++++++-- apps/desktop/src/locales/de/messages.json | 49 +++++++++-- apps/desktop/src/locales/el/messages.json | 49 +++++++++-- apps/desktop/src/locales/en_GB/messages.json | 49 +++++++++-- apps/desktop/src/locales/en_IN/messages.json | 49 +++++++++-- apps/desktop/src/locales/eo/messages.json | 49 +++++++++-- apps/desktop/src/locales/es/messages.json | 49 +++++++++-- apps/desktop/src/locales/et/messages.json | 49 +++++++++-- apps/desktop/src/locales/eu/messages.json | 49 +++++++++-- apps/desktop/src/locales/fa/messages.json | 49 +++++++++-- apps/desktop/src/locales/fi/messages.json | 49 +++++++++-- apps/desktop/src/locales/fil/messages.json | 49 +++++++++-- apps/desktop/src/locales/fr/messages.json | 49 +++++++++-- apps/desktop/src/locales/gl/messages.json | 49 +++++++++-- apps/desktop/src/locales/he/messages.json | 49 +++++++++-- apps/desktop/src/locales/hi/messages.json | 49 +++++++++-- apps/desktop/src/locales/hr/messages.json | 49 +++++++++-- apps/desktop/src/locales/hu/messages.json | 65 +++++++++++---- apps/desktop/src/locales/id/messages.json | 49 +++++++++-- apps/desktop/src/locales/it/messages.json | 87 ++++++++++++++------ apps/desktop/src/locales/ja/messages.json | 49 +++++++++-- apps/desktop/src/locales/ka/messages.json | 49 +++++++++-- apps/desktop/src/locales/km/messages.json | 49 +++++++++-- apps/desktop/src/locales/kn/messages.json | 49 +++++++++-- apps/desktop/src/locales/ko/messages.json | 49 +++++++++-- apps/desktop/src/locales/lt/messages.json | 49 +++++++++-- apps/desktop/src/locales/lv/messages.json | 49 +++++++++-- apps/desktop/src/locales/me/messages.json | 49 +++++++++-- apps/desktop/src/locales/ml/messages.json | 49 +++++++++-- apps/desktop/src/locales/mr/messages.json | 49 +++++++++-- apps/desktop/src/locales/my/messages.json | 49 +++++++++-- apps/desktop/src/locales/nb/messages.json | 49 +++++++++-- apps/desktop/src/locales/ne/messages.json | 49 +++++++++-- apps/desktop/src/locales/nl/messages.json | 49 +++++++++-- apps/desktop/src/locales/nn/messages.json | 49 +++++++++-- apps/desktop/src/locales/or/messages.json | 49 +++++++++-- apps/desktop/src/locales/pl/messages.json | 49 +++++++++-- apps/desktop/src/locales/pt_BR/messages.json | 49 +++++++++-- apps/desktop/src/locales/pt_PT/messages.json | 49 +++++++++-- apps/desktop/src/locales/ro/messages.json | 49 +++++++++-- apps/desktop/src/locales/ru/messages.json | 49 +++++++++-- apps/desktop/src/locales/si/messages.json | 49 +++++++++-- apps/desktop/src/locales/sk/messages.json | 75 ++++++++++++----- apps/desktop/src/locales/sl/messages.json | 49 +++++++++-- apps/desktop/src/locales/sr/messages.json | 49 +++++++++-- apps/desktop/src/locales/sv/messages.json | 49 +++++++++-- apps/desktop/src/locales/te/messages.json | 49 +++++++++-- apps/desktop/src/locales/th/messages.json | 49 +++++++++-- apps/desktop/src/locales/tr/messages.json | 51 ++++++++++-- apps/desktop/src/locales/uk/messages.json | 53 ++++++++++-- apps/desktop/src/locales/vi/messages.json | 49 +++++++++-- apps/desktop/src/locales/zh_CN/messages.json | 49 +++++++++-- apps/desktop/src/locales/zh_TW/messages.json | 49 +++++++++-- 63 files changed, 2752 insertions(+), 421 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index d55416d6fe7..419b2ef191a 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Kies ’n organisasie waarheen u hierdie item wil skuif. Deur te skuif kry die organisasie die einaarskap van die item. U is dan nie meer die direkte eienaar van die item wanneer dit geskuif is nie." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Wek 'n Wagwoord op" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipe" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopieer Wagwoord" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopieer URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Skep Rekening" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Teken Aan" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Dien In" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Bedienerbronadres" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-bedienerbronadres" }, @@ -1218,6 +1247,9 @@ "message": "Kopieer Nommer", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopieer Sekureiteitskode", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Wek 'n gebruikersnaam op" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Gebruikersnaamtipe" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Wek ’n e-posalias op met ’n eksterne aanstuurdiens." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Teks stuur" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 823562b5b8d..9f1d9ca456d 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "اختر المؤسسة التي ترغب في نقل هذا العنصر إليها. يؤدي الانتقال إلى مؤسسة إلى نقل ملكية العنصر إلى تلك المؤسسة. لن تكون المالك المباشر لهذا العنصر بعد نقله." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "توليد كلمة المرور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "نوع" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "نسخ كلمة المرور" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "نسخ الرابط" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "إنشاء حساب" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "تعيين كلمة مرور قوية" }, @@ -556,6 +569,18 @@ "logIn": { "message": "تسجيل الدخول" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "إرسال" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "رابط الخادم" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "رابط خادم API" }, @@ -1218,6 +1247,9 @@ "message": "نسخ الرقم", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "نسخ رمز الأمان", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "إنشاء اسم المستخدم" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع اسم المستخدم" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "إنشاء بريد إلكتروني مستعار مع خدمة إعادة توجيه خارجية." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ خطأ: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "إرسال النص" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 5649f33a2ad..9fc3e92f9de 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Bu elementi daşımaq istədiyiniz təşkilatı seçin. Bir təşkilata daşımaq, elementin sahibliyini də həmin təşkilata daşıyacaq. Daşıdıqdan sonra bu elementə birbaşa sahibliyiniz olmayacaq." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Parol yarat" }, + "generatePassphrase": { + "message": "Keçid ifadələri yarat" + }, "type": { "message": "Növ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Parolu kopyala" }, + "copyPassphrase": { + "message": "Keçid ifadəsini kopyala", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI-ni kopyala" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Hesab yarat" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Güclü bir parol təyin et" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Giriş et" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Göndər" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL-si" }, + "selfHostBaseUrl": { + "message": "Self-host server URL-si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL-si" }, @@ -1218,6 +1247,9 @@ "message": "Nömrəni kopyala", "description": "Copy credit card number" }, + "copyEmail": { + "message": "E-poçtu kopyala" + }, "copySecurityCode": { "message": "Güvənlik kodunu kopyala", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "İstifadəçi adı yarat" }, + "generateEmail": { + "message": "E-poçt yarat" + }, "usernameType": { "message": "İstifadəçi adı növü" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Xarici yönləndirmə xidməti ilə e-poçt ləqəbi yaradın." }, + "forwarderDomainName": { + "message": "E-poçt domeni", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçilmiş xidmət tərəfindən dəstəklənən bir domen seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ xətası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Mətn \"Send\"ləri" }, - "allowScreenshots": { - "message": "Ekranı çəkməyə icazə ver" - }, - "allowScreenshotsDesc": { - "message": "Bitwarden masaüstü tətbiqinin ekranını çəkməyə icazə ver." - }, "ssoError": { "message": "SSO giriş üçün açıq port tapıla bilmədi." }, diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 71935751353..03d5601b074 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Выберыце арганізацыю, у якую вы хочаце перамясціць гэты элемент. Пры перамяшчэнні ў арганізацыю ўсе правы ўласнасці на дадзены элемент пяройдуць да гэтай арганізацыі. Вы больш не будзеце адзіным уласнікам гэтага элемента пасля яго перамяшчэння." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Генерыраваць пароль" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Тып" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Скапіяваць пароль" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Скапіяваць URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Стварыць уліковы запіс" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Увайсці" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Адправіць" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL-адрас сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Сервер URL-адраса API" }, @@ -1218,6 +1247,9 @@ "message": "Скапіяваць нумар", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Скапіяваць код бяспекі", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Генерыраваць імя карыстальніка" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тып імя карыстальніка" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Генерыраваць псеўданім электроннай пошты са знешнім сэрвісам перасылкі." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index db62458f001..34abb4ec47e 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Добре дошли отново" + }, "moveToOrgDesc": { "message": "Изберете организацията, в която искате да преместите записа. Преместването прехвърля собствеността му към новата организация. След това няма вече директно да го притежавате." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Нова парола" }, + "generatePassphrase": { + "message": "Генериране на парола-фраза" + }, "type": { "message": "Вид" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Копиране на паролата" }, + "copyPassphrase": { + "message": "Копиране на паролата-фраза", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Копиране на адреса" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Създаване на абонамент" }, + "newToBitwarden": { + "message": "За пръв път ли ползвате Битуорден?" + }, "setAStrongPassword": { "message": "Използвайте сложна парола" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Вписване" }, + "logInToBitwarden": { + "message": "Впишете се в Битуорден" + }, + "logInWithPasskey": { + "message": "Вписване със секретен ключ" + }, + "loginWithDevice": { + "message": "Вписване с устройство" + }, + "useSingleSignOn": { + "message": "Използване на еднократна идентификация" + }, "submit": { "message": "Подаване" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Адрес на сървъра" }, + "selfHostBaseUrl": { + "message": "Адрес на собствения сървър", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Адрес на ППИ-сървъра" }, @@ -1218,6 +1247,9 @@ "message": "Копиране на но̀мера", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Копиране на е-пощата" + }, "copySecurityCode": { "message": "Копиране на кода за сигурност", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Генериране на потр. име" }, + "generateEmail": { + "message": "Генериране на електронна поща" + }, "usernameType": { "message": "Тип потребителско име" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Създайте псевдоним на е-поща с външна услуга за препращане." }, + "forwarderDomainName": { + "message": "Домейн на електронната поща", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Изберете домейн, който се поддържа от избраната услуга", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Грешка от $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Текстови изпращания" }, - "allowScreenshots": { - "message": "Позволяване на заснемането на екрана" - }, - "allowScreenshotsDesc": { - "message": "Позволяване на заснемането на екрана когато работи настолното приложение на Битуорден." - }, "ssoError": { "message": "Не могат да бъдат открити свободни портове за еднократната идентификация." }, diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index ddc79274023..bd4b2cef542 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "পাসওয়ার্ড তৈরি করুন" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "ধরন" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "পাসওয়ার্ড অনুলিপিত করুন" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI অনুলিপিত করুন" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "অ্যাকাউন্ট তৈরি করুন" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "প্রবেশ করুন" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "জমা দিন" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "সার্ভার URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "এপিআই সার্ভার URL" }, @@ -1218,6 +1247,9 @@ "message": "নম্বর অনুলিপিত করুন", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "সুরক্ষা কোড অনুলিপিত করুন", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 77206ba1cde..8ac7f011f88 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Odaberi organizaciju u koju želiš premjestiti ovu stavku. Premještanje prenosi vlasništvo stavke na organizaciju. Nakon premještanja više nećeš biti direktni vlasnik ove stavke." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generiraj lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Vrsta" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopirajte lozinku" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiraj URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Napravi račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Prijavite se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Potvrdi" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL servera" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API servera" }, @@ -1218,6 +1247,9 @@ "message": "Kopiraj broj", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopirajte sigurnosni kod", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 31d5e33071d..8cf7c735507 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Trieu una organització a la qual vulgueu desplaçar aquest element. El trasllat a una organització transfereix la propietat de l'element a aquesta organització. Ja no sereu el propietari directe d'aquest element una vegada s'haja mogut." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Genera contrasenya" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipus" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copia contrasenya" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copia URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Crea un compte" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Estableix una contrasenya segura" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Inicia sessió" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Envia" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del servidor API" }, @@ -1218,6 +1247,9 @@ "message": "Copia el número", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copia el codi de seguretat", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Genera un nom d'usuari" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipus de nom d'usuari" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Genera un àlies de correu electrònic amb un servei de reenviament extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 1ff6a60e526..a0544cc991f 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Vítejte zpět" + }, "moveToOrgDesc": { "message": "Vyberte organizaci, do které chcete tuto položku přesunout. Přesun do organizace převede vlastnictví položky této organizaci. Po přesunutí této položky již nebudete přímým vlastníkem této položky." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Vygenerovat heslo" }, + "generatePassphrase": { + "message": "Vygenerovat heslovou frázi" + }, "type": { "message": "Typ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopírovat heslo" }, + "copyPassphrase": { + "message": "Kopírovat heslovou frázi", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopírovat URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Vytvořit účet" }, + "newToBitwarden": { + "message": "Jste noví na Bitwardenu?" + }, "setAStrongPassword": { "message": "Nastavit hlavní heslo" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Přihlásit se" }, + "logInToBitwarden": { + "message": "Přihlásit se do Bitwardenu" + }, + "logInWithPasskey": { + "message": "Přihlásit se pomocí přístupového klíče" + }, + "loginWithDevice": { + "message": "Přihlásit se zařízením" + }, + "useSingleSignOn": { + "message": "Použít jednotné přihlášení" + }, "submit": { "message": "Odeslat" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL serveru" }, + "selfHostBaseUrl": { + "message": "Adresa URL serveru vlastního hostování", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL serveru API" }, @@ -1218,6 +1247,9 @@ "message": "Kopírovat číslo", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Kopírovat e-mail" + }, "copySecurityCode": { "message": "Kopírovat bezpečnostní kód", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Vygenerovat uživatelské jméno" }, + "generateEmail": { + "message": "Vygenerovat e-mail" + }, "usernameType": { "message": "Typ uživatelského jména" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Vygeneruje alias e-mailu pomocí externí přeposílací služby." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, která je podporována vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Chyba $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Sends s texty" }, - "allowScreenshots": { - "message": "Povolit záznam obrazovky" - }, - "allowScreenshotsDesc": { - "message": "Povolí zachycování obrazovky desktopové aplikace Bitwarden." - }, "ssoError": { "message": "Pro přihlášení SSO nebyly nalezeny žádné volné porty." }, diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index a1902379c83..17be35180dd 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 383354aa8a8..36c8d305469 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Velkommen tilbage" + }, "moveToOrgDesc": { "message": "Vælg en organisation, hvortil dette emne skal flyttes. Flytning overfører ejerskab af emnet til organisationen, og efter flytningen vil man ikke længere være den direkte ejer af emnet." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generér adgangskode" }, + "generatePassphrase": { + "message": "Generér adgangssætning" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiér adgangskode" }, + "copyPassphrase": { + "message": "Kopiér adgangssætning", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiér URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Opret konto" }, + "newToBitwarden": { + "message": "Ny på Bitwarden?" + }, "setAStrongPassword": { "message": "Indstil en stærk adgangskode" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log ind" }, + "logInToBitwarden": { + "message": "Log ind på Bitwarden" + }, + "logInWithPasskey": { + "message": "Log ind med adgangsnøgle" + }, + "loginWithDevice": { + "message": "Log ind med enhed" + }, + "useSingleSignOn": { + "message": "Brug Single Sign-On" + }, "submit": { "message": "Indsend" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "URL til selv-hostet server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-server URL" }, @@ -1218,6 +1247,9 @@ "message": "Kopiér nummer", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Kopiér e-mail" + }, "copySecurityCode": { "message": "Kopiér bekræftelseskode", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generér brugernavn" }, + "generateEmail": { + "message": "Generér e-mail" + }, "usernameType": { "message": "Brugernavnstype" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generér et e-mail alias med en ekstern viderestillingstjeneste." }, + "forwarderDomainName": { + "message": "E-maildomæne", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vælg et domæne understøttet af den valgte tjeneste", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fejl: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Tekst-Sends" }, - "allowScreenshots": { - "message": "Tillad skærmoptagelse" - }, - "allowScreenshotsDesc": { - "message": "Tillad skærmoptagelse af Bitwarden-computerprogrammet." - }, "ssoError": { "message": "Ingen ledige porte fundet til SSO-login." }, diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 7ea2bd8b1cb..6679ad492ba 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Wähle eine Organisation aus, in die du diesen Eintrag verschieben möchtest. Das Verschieben in eine Organisation überträgt das Eigentum an diese Organisation. Du bist nicht mehr der direkte Eigentümer dieses Eintrags, sobald er verschoben wurde." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Passwort generieren" }, + "generatePassphrase": { + "message": "Passphrase generieren" + }, "type": { "message": "Typ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Passwort kopieren" }, + "copyPassphrase": { + "message": "Passphrase kopieren", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI kopieren" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Konto erstellen" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Lege ein starkes Passwort fest" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Anmelden" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Absenden" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "Selbst gehostete Server-URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server-URL" }, @@ -1218,6 +1247,9 @@ "message": "Nummer kopieren", "description": "Copy credit card number" }, + "copyEmail": { + "message": "E-Mail-Adresse kopieren" + }, "copySecurityCode": { "message": "Sicherheitscode kopieren", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Benutzername generieren" }, + "generateEmail": { + "message": "E-Mail generieren" + }, "usernameType": { "message": "Benutzernamentyp" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Erzeuge ein E-Mail-Alias mit einem externen Weiterleitungsdienst." }, + "forwarderDomainName": { + "message": "E-Mail-Domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Wähle eine Domain aus, die vom ausgewählten Dienst unterstützt wird", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ Fehler: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text-Sends" }, - "allowScreenshots": { - "message": "Bildschirmaufnahme erlauben" - }, - "allowScreenshotsDesc": { - "message": "Bildschirmaufnahme der Bitwarden Desktop-Anwendung erlauben." - }, "ssoError": { "message": "Es konnten keine freien Ports für die SSO-Anmeldung gefunden werden." }, diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 98d5d1c851f..6f92a5dd714 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Επιλέξτε έναν οργανισμό στον οποίο θέλετε να μετακινήσετε αυτό το στοιχείο. Η μετακίνηση σε έναν οργανισμό μεταβιβάζει την ιδιοκτησία του στοιχείου σε αυτό τον οργανισμό. Δεν θα είστε πλέον ο άμεσος ιδιοκτήτης αυτού του στοιχείου μόλις το μετακινήσετε." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Γέννηση κωδικού πρόσβασης" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Τύπος" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Αντιγραφή κωδικού πρόσβασης" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Αντιγραφή URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Δημιουργία λογαριασμού" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Ορίστε έναν ισχυρό κωδικό πρόσβασης" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Είσοδος" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Υποβολή" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL Διακομιστή" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL διακομιστή API" }, @@ -1218,6 +1247,9 @@ "message": "Αντιγραφή αριθμού", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Αντιγραφή κωδικού ασφαλείας", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Δημιουργία ονόματος χρήστη" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Τύπος ονόματος χρήστη" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Δημιουργήστε ένα alias email με μια εξωτερική υπηρεσία προώθησης." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ σφάλμα: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Send κειμένων" }, - "allowScreenshots": { - "message": "Να επιτρέπεται η καταγραφή οθόνης" - }, - "allowScreenshotsDesc": { - "message": "Να επιτρέπεται η καταγραφή οθόνης της εφαρμογής Bitwarden για υπολογιστή." - }, "ssoError": { "message": "Δεν βρέθηκαν ελεύθερες θύρες για τη σύνδεση sso." }, diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index e12153fb6c7..04b367afa97 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organisation that you wish to move this item to. Moving to an organisation transfers ownership of the item to that organisation. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 443ef7276d2..6ccdf450f79 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organisation that you wish to move this item to. Moving to an organisation transfers ownership of the item to that organistaion. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index fb1e07da35f..92ba6047bba 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Elektu organizaĵon, al kiu vi volas movi ĉi tiun eron. Movado al organizaĵo transdonas la posedon de la ero al tiu organizaĵo. Vi ne plu estos la rekta posedanto de la ero post kiam ĝi estos movita." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipo" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Ensaluti" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 90412da5003..90ba8426d7c 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Elige una organización a la que deseas mover este objeto. Moviendo a una organización transfiere la propiedad del objeto a esa organización. Ya no serás el dueño directo de este objeto una vez que haya sido movido." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generar contraseña" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipo" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copiar contraseña" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copiar URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Crear cuenta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Establece una contraseña fuerte" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Identificarse" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Enviar" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del servidor de la API" }, @@ -1218,6 +1247,9 @@ "message": "Copiar número", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copiar código de seguridad", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generar nombre de usuario" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de nombre de usuario" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generar un alias de correo electrónico con un servicio de reenvío externo." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Envío de texto" }, - "allowScreenshots": { - "message": "Permitir captura de pantalla" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 9c6f912aefe..fc51b2ac583 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Vali organisatsioon, kuhu soovid seda kirjet teisaldada. Teisaldamisega saab kirje omanikuks organisatsioon. Pärast kirje teisaldamist ei ole sa enam selle otsene omanik." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Loo parool" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tüüp" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopeeri parool" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopeeri URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Konto loomine" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Määra tugev parool" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Logi sisse" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Kinnita" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Serveri URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API serveri URL" }, @@ -1218,6 +1247,9 @@ "message": "Kopeeri number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopeeri turvakood", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Genereeri kasutajanimi" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Kasutajanime tüüp" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Genereeri e-posti alias, kasutades selleks välist teenuspakkujat." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Kõik Tekst-Sendid" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "SSO-ga sisselogimiseks ei leitud ühtegi vaba porti." }, diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 039ab991467..056b543f28f 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Aukeratu elementu hau zein erakundetara eraman nahi duzun. Erakunde batera pasatzeak elementuaren jabetza erakunde horretara transferitzen du. Zu ez zara elementu honen jabe zuzena izango mugitzen duzunean." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Sortu pasahitza" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Mota" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiatu pasahitza" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiatu URIa" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Sortu kontua" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Hasi saioa" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Bidali" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Zerbitzariaren URL-a" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API zerbitzariaren URL-a" }, @@ -1218,6 +1247,9 @@ "message": "Kopiatu zenbakia", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopiatu segurtasun-kodea", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Sortu erabiltzaile izena" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Erabiltzaile izen mota" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Emaileko ezizen bat sortu kanpoko bidalketa zerbitzu batekin." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 677655d84d0..c17a1faad0e 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "سازمانی را انتخاب کنید که می‌خواهید این مورد را به آن منتقل کنید. انتقال به یک سازمان، مالکیت مورد را به آن سازمان منتقل می‌کند. پس از انتقال این مورد، دیگر مالک مستقیم آن نخواهید بود." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "تولید کلمه عبور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "نوع" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "کپی کلمه عبور" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "کپی نشانی اینترنتی" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "ایجاد حساب کاربری" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "تنظیم رمز عبور قوی" }, @@ -556,6 +569,18 @@ "logIn": { "message": "ورود" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "ثبت" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "نشانی اینترنتی سرور" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "نشانی سرور API" }, @@ -1218,6 +1247,9 @@ "message": "کپی شماره", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "کپی کد امنیتی", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "ایجاد نام کاربری" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع نام کاربری" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "یک نام مستعار ایمیل با یک سرویس ارسال خارجی ایجاد کنید." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index e6f03da4f45..ebd2ba29a76 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Valitse organisaatio, jolle haluat siirtää kohteen. Tämä siirtää kohteen organisaation omistukseen, etkä tämän jälkeen ole enää sen suora omistaja." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Luo salasana" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tyyppi" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopioi salasana" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopioi URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Luo tili" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Aseta vahva salasana" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Kirjaudu" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Jatka" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Palvelimen URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-palvelimen URL" }, @@ -1218,6 +1247,9 @@ "message": "Kopioi numero", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopioi turvakoodi", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Luo käyttäjätunnus" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Käyttäjätunnuksen tyyppi" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Luo sähköpostialias ulkoisella ohjauspalvelulla." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ -virhe: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Teksti-Sendit" }, - "allowScreenshots": { - "message": "Salli kuvankaappaus" - }, - "allowScreenshotsDesc": { - "message": "Salli kuvankaappaus Bitwardenin työpöytäsovelluksesta." - }, "ssoError": { "message": "Kertakirjautumiselle ei löytynyt vapaita portteja." }, diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index e699ab143bf..16f1bb0c043 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Pumili ng isang organisasyon kung saan mo nais na ipalipat ang item na ito. Ang paglipat sa isang organisasyon ay nagpapalipat ng pagmamay-ari ng item sa organisasyon na iyon. Hindi ka na magiging direktang may-ari ng item na ito kapag naipadala na ito." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Magtatag ng Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Uri" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopyahin ang password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopyahin ang URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Gumawa ng Account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Mag-log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Isumite" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL ng Server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL ng API server" }, @@ -1218,6 +1247,9 @@ "message": "Pamamagitan ng Kopya ng Bilang", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopyahin ang code ng seguridad", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Lumikha ng username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Uri ng username" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Bumuo ng isang email alias na may isang panlabas na serbisyo sa pagpapasa." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 2c22ee211bb..84964e0d3bf 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Content de vous revoir" + }, "moveToOrgDesc": { "message": "Choisissez une organisation vers laquelle vous souhaitez déplacer cet élément. Déplacer un élément vers une organisation transfère la propriété de l'élément à cette organisation. Vous ne serez plus le propriétaire direct de cet élément une fois qu'il aura été déplacé." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Générer un mot de passe" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copier le mot de passe" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copier l'URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Créer un compte" }, + "newToBitwarden": { + "message": "Nouveau sur Bitwarden ?" + }, "setAStrongPassword": { "message": "Définir un mot de passe fort" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Se connecter" }, + "logInToBitwarden": { + "message": "Se connecter à Bitwarden" + }, + "logInWithPasskey": { + "message": "Se connecter avec une clé d'accès" + }, + "loginWithDevice": { + "message": "Se connecter avec l'appareil" + }, + "useSingleSignOn": { + "message": "Utiliser l'authentification unique" + }, "submit": { "message": "Soumettre" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL du serveur" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL du serveur de l'API" }, @@ -1218,6 +1247,9 @@ "message": "Copier le numéro", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copier le code de sécurité", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Générer le nom d'utilisateur" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Type de nom d'utilisateur" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Générer un alias de courriel avec un service de transfert externe." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erreur de $SERVICENAME$ : $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Autoriser les captures d'écran" - }, - "allowScreenshotsDesc": { - "message": "Autoriser la capture d'écran de l'application de bureau Bitwarden." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 9924a91fa36..64109a052c6 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 182925d1410..a7d2b0990ea 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "בחר ארגון שאליו תרצה להעביר פריט זה. העברה לארגון מעבירה את הבעלות על הפריט לאותו ארגון. לאחר העברת פריט זה לא תהיה יותר הבעלים הישיר." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "צור סיסמה" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "סוג" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "העתק סיסמה" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "העתק שורת כתובת" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "צור חשבון" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "הגדרת סיסמה חזקה" }, @@ -556,6 +569,18 @@ "logIn": { "message": "התחבר" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "שלח" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "כתובת שרת" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "כתובת שרת הAPI" }, @@ -1218,6 +1247,9 @@ "message": "העתק מספר", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "העתק קוד אבטחה", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "חולל שם משתמש" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "סוג שם משתמש" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "צור כינוי דוא\"ל באמצעות שירות שליחת דוא\"ל חיצוני." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 21c978cac14..74f0f7eef91 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index b6a3f011482..d121600b024 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Odaberi organizaciju u koju želiš premjestiti ovu stavku. Premještanje prenosi vlasništvo stavke na organizaciju. Nakon premještanja više nećeš biti izravni vlasnik ove stavke." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generiraj lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Vrsta" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiraj lozinku" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiraj URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Stvori račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Postavi jaku lozinku" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Prijavi se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Pošalji" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL poslužitelja" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API poslužitelja" }, @@ -1218,6 +1247,9 @@ "message": "Kopiraj broj", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopiraj kontrolni broj", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generiraj korisničko ime" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip korisničkog imena" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ greška: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Send tekstovi" }, - "allowScreenshots": { - "message": "Dozvoli snimanje zaslona" - }, - "allowScreenshotsDesc": { - "message": "Dozvoli snimanje zaslona Bitwarden aplikacije za stolna računala." - }, "ssoError": { "message": "Nisu nađeni slobodni portovi za SSO prijavu." }, diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index a3e79f8f63a..f218fa6a621 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Üdvözlet újra" + }, "moveToOrgDesc": { "message": "Válasszunk egy szervezetet, ahová áthelyezni szeretnénk ezt az elemet. A szervezetbe áthelyezés átruházza az elem tulajdonjogát az adott szervezetre. Az áthelyezés után többé nem leszünk az elem közvetlen tulajdonosa." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Jelszó generálása" }, + "generatePassphrase": { + "message": "Jelmondat generálás" + }, "type": { "message": "Típus" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Jelszó másolása" }, + "copyPassphrase": { + "message": "Jelmondat másolása", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI másolása" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Fiók létrehozása" }, + "newToBitwarden": { + "message": "Új vagyunk a Bitwardenben?" + }, "setAStrongPassword": { "message": "Erős jelszó beállítása" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Bejelentkezés" }, + "logInToBitwarden": { + "message": "Bejelentkezés a Bitwardenbe" + }, + "logInWithPasskey": { + "message": "Bejelentkezés azonosító kulcssal" + }, + "loginWithDevice": { + "message": "Bejelentkezés eszközzel" + }, + "useSingleSignOn": { + "message": "Egyszeri bejelentkezés használata" + }, "submit": { "message": "Beküldés" }, @@ -603,7 +628,7 @@ "message": "Csatlakozás szervezethez" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Csatlakozás: $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -828,6 +853,10 @@ "baseUrl": { "message": "Szerver webcím" }, + "selfHostBaseUrl": { + "message": "Saját üzemeltetésű szerver webcím", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API szerver webcím" }, @@ -1218,6 +1247,9 @@ "message": "Szám másolása", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Email cím másolása" + }, "copySecurityCode": { "message": "Biztonsági kód másolása", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Felhasználónév generálása" }, + "generateEmail": { + "message": "Email generálása" + }, "usernameType": { "message": "Felhasználónév típusa" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Email álnév generálása külső továbbító szolgáltatással." }, + "forwarderDomainName": { + "message": "Email tartomány", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Válasszunk a kiválasztott szolgáltatás által támogatott tartományt.", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hiba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -2875,19 +2918,19 @@ "message": "Almenü" }, "toggleSideNavigation": { - "message": "Toggle side navigation" + "message": "Oldal navigáció váltás" }, "skipToContent": { "message": "Ugrás a tartalomra" }, "typePasskey": { - "message": "Hozzáférési kulcs" + "message": "Azonosító kulcs" }, "passkeyNotCopied": { - "message": "A hozzáférési kulcs nem kerül másolásra." + "message": "Az azonosító kulcs nem kerül másolásra." }, "passkeyNotCopiedAlert": { - "message": "A hozzáférési kulcs nem kerül másolásra a klónozott elembe. Folytatjuk ennek az elemnek a klónozását?" + "message": "Az azonosító kulcs nem kerül másolásra a klónozott elembe. Folytatjuk ennek az elemnek a klónozását?" }, "aliasDomain": { "message": "Áldomain" @@ -3089,7 +3132,7 @@ "message": "Gyűjtemény" }, "lastPassYubikeyDesc": { - "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + "message": "Illesszük be a LastPass fiókhoz társított YubiKey eszközt a számítógép USB portjába, majd érintsük meg annak gombját." }, "commonImportFormats": { "message": "Általános formátumok", @@ -3108,10 +3151,10 @@ "message": "A hardveres gyorsítás engedélyezése és újraindítás" }, "removePasskey": { - "message": "Remove passkey" + "message": "Azonosító kulcs eltávolítás" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "Az azonosító kucs eltávolításra került." }, "errorAssigningTargetCollection": { "message": "Hiba történt a célgyűjtemény hozzárendelése során." @@ -3162,12 +3205,6 @@ "textSends": { "message": "Szöveg küldés" }, - "allowScreenshots": { - "message": "Képernyőrögzítés engedélyezése" - }, - "allowScreenshotsDesc": { - "message": "A Bitwarden asztali alkalmazás képernyőfelvételének engedélyezése." - }, "ssoError": { "message": "Nem található szabad port az sso bejelentkezéshez." }, diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 46195128ca4..3ca3f06ce1a 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Pilihlah sebuah organisasi yang Anda ingin memindahkan item ini. Memindahkan berarti memberikan kepemilikan kepada organisasi tersebut. Anda tidak akan lagi menjadi pemilik item ini." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Buat Kata Sandi" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipe" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Salin Kata Sandi" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Salin URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Buat Akun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Masuk" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Kirim" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL Server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL Server API" }, @@ -1218,6 +1247,9 @@ "message": "Salin Nomor", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Salin Kode Keamanan", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Buat nama pengguna baru" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Jenis nama pengguna" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 2e8ea544d24..eac15e7e4bc 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Scegli un'organizzazione in cui vuoi spostare questo elemento. Spostarlo in un'organizzazione trasferisce la proprietà dell'elemento all'organizzazione. Una volta spostato, non sarai più il proprietario diretto di questo elemento." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Genera password" }, + "generatePassphrase": { + "message": "Genera passphrase" + }, "type": { "message": "Tipo" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copia password" }, + "copyPassphrase": { + "message": "Copia passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copia URI" }, @@ -422,11 +432,11 @@ "message": "Caratteri speciali (!@#$%^&*)" }, "include": { - "message": "Include", + "message": "Includi", "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "Includi caratteri maiuscoli", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -434,7 +444,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "Includi caratteri minuscoli", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -442,7 +452,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Includi numeri", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -450,7 +460,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Includi caratteri speciali", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -485,11 +495,11 @@ "description": "deprecated. Use avoidAmbiguous instead." }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "Evita caratteri ambigui", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "I requisiti di politica aziendale sono stati applicati alle opzioni del generatore.", "description": "Indicates that a policy limits the credential generator screen." }, "searchCollection": { @@ -547,6 +557,9 @@ "createAccount": { "message": "Crea account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Imposta una password robusta" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Accedi" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Invia" }, @@ -603,7 +628,7 @@ "message": "Unisciti all'organizzazione" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Unisciti a $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL del server" }, + "selfHostBaseUrl": { + "message": "URL server autogestito", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del server API" }, @@ -972,16 +1001,16 @@ "message": "La tua cassaforte è bloccata. Verifica la tua identità per continuare." }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "Il tuo account è bloccato" }, "or": { - "message": "or" + "message": "o" }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "Sblocca con dati biometrici" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "Sblocca con la password principale" }, "unlock": { "message": "Sblocca" @@ -1012,7 +1041,7 @@ "message": "Timeout cassaforte" }, "vaultTimeout1": { - "message": "Timeout" + "message": "Tempo limite" }, "vaultTimeoutDesc": { "message": "Scegli quando la tua cassaforte eseguirà l'azione di timeout." @@ -1218,6 +1247,9 @@ "message": "Copia numero", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copia e-mail" + }, "copySecurityCode": { "message": "Copia codice di sicurezza", "description": "Copy credit card security code (CVV)" @@ -1640,7 +1672,7 @@ "message": "Consigliato per la sicurezza." }, "lockWithMasterPassOnRestart1": { - "message": "Lock with master password on restart" + "message": "Blocca con password principale al riavvio" }, "deleteAccount": { "message": "Elimina account" @@ -1762,7 +1794,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "di $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -1850,7 +1882,7 @@ "message": "Consenti l'integrazione del browser" }, "enableBrowserIntegrationDesc1": { - "message": "Used to allow biometric unlock in browsers that are not Safari." + "message": "Usato per consentire lo sblocco biometrico nei browser che non sono Safari." }, "enableDuckDuckGoBrowserIntegration": { "message": "Consenti l'integrazione del browser DuckDuckGo" @@ -2181,7 +2213,7 @@ "message": "Minuti" }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "Al massimo $HOURS$ ora/e e $MINUTES$ minuto/i.", "placeholders": { "hours": { "content": "$1", @@ -2331,7 +2363,7 @@ "message": "Bloccato" }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "Cassaforte bloccata" }, "unlocked": { "message": "Sbloccato" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Genera nome utente" }, + "generateEmail": { + "message": "Genera e-mail" + }, "usernameType": { "message": "Tipo di nome utente" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Genera un alias email con un servizio di inoltro esterno." }, + "forwarderDomainName": { + "message": "Dominio e-mail", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Scegli un dominio supportato dal servizio selezionato", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Errore $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -2687,7 +2730,7 @@ "message": "Importante:" }, "accessing": { - "message": "Accessing" + "message": "Accesso" }, "accessTokenUnableToBeDecrypted": { "message": "Sei stato fatto uscire perché non è stato possibile decrittografare il tuo token di accesso. Entra di nuovo per risolvere il problema." @@ -3162,12 +3205,6 @@ "textSends": { "message": "Send Testo" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "Non è stato possibile trovare nessuna porta libera per il login Sso." }, diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 5b27a9ce615..9c5ec2e9fc3 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "このアイテムを移動する組織を選択してください。組織に移動すると、アイテムの所有権がその組織に移行します。 このアイテムが移動された後、あなたはこのアイテムの直接の所有者にはなりません。" }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "パスワードの自動生成" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "タイプ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "パスワードのコピー" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI のコピー" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "アカウントの作成" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "強力なパスワードを設定する" }, @@ -556,6 +569,18 @@ "logIn": { "message": "ログイン" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "送信" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "サーバー URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API サーバー URL" }, @@ -1218,6 +1247,9 @@ "message": "番号のコピー", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "セキュリティコードのコピー", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "ユーザー名を生成" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "ユーザー名の種類" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "外部転送サービスを使用してメールエイリアスを生成します。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ エラー: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "テキスト Send" }, - "allowScreenshots": { - "message": "スクリーンショットを許可" - }, - "allowScreenshotsDesc": { - "message": "Bitwarden デスクトップアプリのスクリーンショットを許可します。" - }, "ssoError": { "message": "SSO ログインのための空きポートが見つかりませんでした。" }, diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 529f65cf4ca..49179259473 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "პაროლის გენერირება" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "ტიპი" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "პაროლის კოპირება" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "ანგარიშის შექმნა" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "შესვლა" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "გადაცემა" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "სერვერის URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 9924a91fa36..64109a052c6 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 85fb3c916ca..ba5059aaa3a 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "ಈ ಐಟಂ ಅನ್ನು ಸರಿಸಲು ನೀವು ಬಯಸುವ ಸಂಸ್ಥೆಯನ್ನು ಆರಿಸಿ. ಸಂಸ್ಥೆಗೆ ಹೋಗುವುದರಿಂದ ವಸ್ತುವಿನ ಮಾಲೀಕತ್ವವನ್ನು ಆ ಸಂಸ್ಥೆಗೆ ವರ್ಗಾಯಿಸುತ್ತದೆ. ಈ ಐಟಂ ಅನ್ನು ಸರಿಸಿದ ನಂತರ ನೀವು ಇನ್ನು ಮುಂದೆ ಅದರ ನೇರ ಮಾಲೀಕರಾಗಿರುವುದಿಲ್ಲ." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "ಪ್ರಕಾರ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "ಪಾಸ್ವರ್ಡ್ ನಕಲಿಸಿ" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI ಅನ್ನು ನಕಲಿಸಿ" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "ಖಾತೆ ತೆರೆ" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "ಲಾಗಿನ್" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "ಒಪ್ಪಿಸು" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "ಸರ್ವರ್ URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API ಸರ್ವರ್ URL" }, @@ -1218,6 +1247,9 @@ "message": "ನಕಲು ಸಂಖ್ಯೆ", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "ಭದ್ರತಾ ಕೋಡ್ ಅನ್ನು ನಕಲಿಸಿ", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 01729030244..adde12a5e92 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "이 항목을 이동할 조직을 선택하십시오. 항목이 조직으로 이동되면 소유권이 조직으로 이전됩니다. 일단 이동되면, 더는 이동된 항목의 직접적인 소유자가 아니게 됩니다." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "비밀번호 생성" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "유형" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "비밀번호 복사" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI 복사" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "계정 만들기" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "로그인" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "보내기" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "서버 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 서버 URL" }, @@ -1218,6 +1247,9 @@ "message": "번호 복사", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "보안 코드 복사", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "아이디 생성" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "아이디 유형" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "외부 포워딩 서비스를 사용해서 이메일 주소 별칭을 만들어보세요." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index ed795800bd3..90b47fe5564 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Pasirinkite organizaciją, į kurią norite priskirti šį elementą. Priskiriant elementą organizacijai, visos elemento valdymo teisės bus perleistos tai organizacijai. Kai elementas bus perkeltas, nebebūsite tiesioginis šio elemento savininkas." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Sugeneruoti slaptažodį" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipas" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopijuoti slaptažodį" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopijuoti nuorodą" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Sukurti paskyrą" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Prisijungti" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Išsaugoti" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Serverio nuoroda" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API serverio nuoroda" }, @@ -1218,6 +1247,9 @@ "message": "Kopijuoti numerį", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopijuoti saugos kodą", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Sugeneruoti naują vartotojo vardą" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Vartotojo vardo tipas" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Sugeneruoti el. pašto slapyvardį su išorine persiuntimo paslauga." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 968bdc3c3d4..d9d16d58b48 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Laipni lūdzam atpakaļ" + }, "moveToOrgDesc": { "message": "Izvēlies apvienību, uz kuru pārvietot šo vienumu. Pārvietošana nodod šī vienuma piederību apvienībai. Tu vairs nebūsi šī vienuma tiešais īpašnieks pēc tā pārvietošanas." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Veidot paroli" }, + "generatePassphrase": { + "message": "Izveidot paroles vārdkopu" + }, "type": { "message": "Veids" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Ievietot paroli starpliktuvē" }, + "copyPassphrase": { + "message": "Ievietot paroles vārdkopu starpliktuvē", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Ievietot vietrādi starpliktuvē" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Izveidot kontu" }, + "newToBitwarden": { + "message": "Bitwarden iepriekš nav izmantots?" + }, "setAStrongPassword": { "message": "Jāiestata droša parole" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Pieteikties" }, + "logInToBitwarden": { + "message": "Pieteikties Bitwarden" + }, + "logInWithPasskey": { + "message": "Pieteikties ar piekļuves atslēgu" + }, + "loginWithDevice": { + "message": "Pieteikties ar ierīci" + }, + "useSingleSignOn": { + "message": "Izmantot vienoto pieteikšanos" + }, "submit": { "message": "Iesniegt" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Servera URL" }, + "selfHostBaseUrl": { + "message": "Pašmitināta servera URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API servera URL" }, @@ -1218,6 +1247,9 @@ "message": "Ievietot numuru starpliktuvē", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Ievietot e-pasta adresi starpliktuvē" + }, "copySecurityCode": { "message": "Ievietot drošības kodu starpliktuvē", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Izveidot lietotājvārdu" }, + "generateEmail": { + "message": "Izveidot e-pastu" + }, "usernameType": { "message": "Lietotājvārda veids" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Izveidot e-pastu aizstājvārdu ar ārēju pārvirzīšanas pakalpojumu." }, + "forwarderDomainName": { + "message": "E-pasta domēns", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Jāizvēlas domēns, kuru atbalsta atlasītais pakalpojums", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ kļūda: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Teksta Send" }, - "allowScreenshots": { - "message": "Atļaut ekrāna tveršanu" - }, - "allowScreenshotsDesc": { - "message": "Atļaut Bitwarden darbvirsmas lietotnes ekrāna tveršanu." - }, "ssoError": { "message": "Netika atrasti brīvi vienotās (SSO) pieteikšanās porti." }, diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index cdd01085bb1..0140da6eb90 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Izaberite organizaciju u koju želite da premjestite ovu stavku. Prelazak u organizaciju prenosi vlasništvo nad stavkom na tu organizaciju. Nećete više biti direktan vlasnik ove stavke kada bude premještena." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generiši lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tip" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiraj lozinku" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiraj link" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Kreiraj nalog" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Prijavi se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Podnesi" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Kopiraj broj", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopiraj siguronosni kod", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 390b63c3bdb..01ce328739c 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "പാസ്‌വേഡ് സൃഷ്ടിക്കുക" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "തരം" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "പാസ്‌വേഡ് പകർത്തുക" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URL പകർത്തുക" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "അക്കൗണ്ട് സൃഷ്ടിക്കുക" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "പ്രവേശിക്കുക" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "സമർപ്പിക്കുക" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "സെർവർ യു ർ ൽ" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API സെർവർ URL" }, @@ -1218,6 +1247,9 @@ "message": "അക്കം പകർത്തുക", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "സുരക്ഷാ കോഡ് പകർത്തുക", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 9924a91fa36..64109a052c6 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 5e4262c7bed..d1266f1225a 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index b9b705b8bb4..3c970433d66 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Velg en organisasjon som du ønsker å flyttet elementet til. Ved å flytte til en organisasjon, overfører du eierskapet til elementet til den organisasjonen. Du vil ikke lenger være den direkte eieren av elementet etter at den har blitt flyttet." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generer et passord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopier passordet" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopier URIen" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Opprett en konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Velg et sterkt passord" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Logg på" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Send inn" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Tjener-nettadresse" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-tjenernettadresse" }, @@ -1218,6 +1247,9 @@ "message": "Kopier nummeret", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopier sikkerhetskoden", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generer brukernavn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Brukernavntype" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generer et e-postalias med en ekstern videresendingstjeneste." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index c4e386d4ab5..6d5a1dca8a5 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "तपाईंले यो आइटम सार्न चाहनुहुने संस्था छान्नुहोस्। संस्थामा सर्दा आइटमको स्वामित्व त्यो संस्थामा हस्तान्तरण हुन्छ। एकपटक सारिएपछि तपाईं अब यस आइटमको प्रत्यक्ष मालिक हुनुहुनेछैन।" }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "पासवर्ड प्रतिलिपि गर्नुहोस्" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI प्रतिलिपि गर्नुहोस्" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 0d19efc9e45..e1fab54089e 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welkom terug" + }, "moveToOrgDesc": { "message": "Kies een organisatie waarnaar je dit item wilt verplaatsen. Door het verplaatsen krijgt de organisatie de eigendomsrechten van het item. Je bent niet langer de directe eigenaar meer van het item als het is verplaatst." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Genereer wachtwoord" }, + "generatePassphrase": { + "message": "Wachtwoordzin genereren" + }, "type": { "message": "Categorie" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopieer wachtwoord" }, + "copyPassphrase": { + "message": "Wachtwoordzin kopiëren", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI kopiëren" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Maak een account aan" }, + "newToBitwarden": { + "message": "Nieuw bij Bitwarden?" + }, "setAStrongPassword": { "message": "Stel een sterk wachtwoord in" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Inloggen op Bitwarden" + }, + "logInWithPasskey": { + "message": "Inloggen met passkey" + }, + "loginWithDevice": { + "message": "Inloggen met apparaat" + }, + "useSingleSignOn": { + "message": "Single sign-on gebruiken" + }, "submit": { "message": "Opslaan" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "URL zelfgehoste server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server-URL" }, @@ -1218,6 +1247,9 @@ "message": "Nummer kopiëren", "description": "Copy credit card number" }, + "copyEmail": { + "message": "E-mail kopiëren" + }, "copySecurityCode": { "message": "Beveiligingscode kopiëren", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Gebruikersnamen genereren" }, + "generateEmail": { + "message": "E-mailadres genereren" + }, "usernameType": { "message": "Type gebruikersnaam" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Genereer een e-mailalias met een externe doorstuurservice." }, + "forwarderDomainName": { + "message": "E-maildomein", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Kies een domein dat wordt ondersteund door de geselecteerde dienst", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ fout: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Tekst-Sends" }, - "allowScreenshots": { - "message": "Schermopname toestaan" - }, - "allowScreenshotsDesc": { - "message": "Schermopnames van de Bitwarden-desktopapplicatie toestaan." - }, "ssoError": { "message": "Er zijn geen vrije poorten gevonden voor de sso-login." }, diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index ae2643910a4..23fa6d498b0 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Vel ein organisasjon du ynskjer å flytte denne oppføringa til. Ved å flytta ei oppføring til ein organisasjon, overfører ein eigarskapen til oppføringa, til den organisasjonen. Du vil ikkje lenger ha eigarskap over denne oppføringa, etter ho er flytta." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generer passord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopier passordet" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Skriv av URI-en" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Opprett konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Logg inn" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Send inn" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Nettadresse for tenar" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Nettadresse for API" }, @@ -1218,6 +1247,9 @@ "message": "Kopier nummer", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopier tryggleikskode", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 9d1ef8088e6..a51f8666b09 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "ପ୍ରକାର" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 5c1b95e9078..8fe52888497 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Wybierz organizację, do której chcesz przenieść ten element. Ta czynność spowoduje utratę własności elementu i przenosi te uprawnienia do organizacji." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Wygeneruj hasło" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Rodzaj" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiuj hasło" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiuj URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Utwórz konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Ustaw silne hasło" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Zaloguj się" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Wyślij" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Adres URL serwera" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Adres URL serwera API" }, @@ -1218,6 +1247,9 @@ "message": "Kopiuj numer", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopiuj kod zabezpieczający", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Wygeneruj nazwę użytkownika" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Rodzaj nazwy użytkownika" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekazywania." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Błąd $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Pozwól na robienie zrzutów ekranu" - }, - "allowScreenshotsDesc": { - "message": "Zezwalaj na przechwytywanie ekranu aplikacji desktopowej Bitwarden." - }, "ssoError": { "message": "Nie znaleziono wolnych portów dla logowania SSO." }, diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index d6cffde5ed1..4ab8cf9629c 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Escolha uma organização para a qual deseja mover este item. Mudar para uma organização transfere a propriedade do item para essa organização. Você não será mais o proprietário direto deste item depois que ele for movido." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Gerar Senha" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tipo" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copiar Senha" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copiar URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Criar Conta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Defina uma senha forte" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Iniciar sessão" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Enviar" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL do Servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do Servidor da API" }, @@ -1218,6 +1247,9 @@ "message": "Copiar Número", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copiar Código de Segurança", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Gerar usuário" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de usuário" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Gere um alias de e-mail com um serviço externo de encaminhamento." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Texto enviado" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "Nenhuma porta livre foi encontrada para o cliente final." }, diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 8182a455158..443c13c0a90 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Bem-vindo de volta" + }, "moveToOrgDesc": { "message": "Escolha uma organização para a qual pretende mover este item. Mover para uma organização transfere a propriedade do item para essa organização. Deixará de ser o proprietário direto deste item depois de este ter sido movido." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Gerar palavra-passe" }, + "generatePassphrase": { + "message": "Gerar frase de acesso" + }, "type": { "message": "Tipo" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copiar palavra-passe" }, + "copyPassphrase": { + "message": "Copiar frase de acesso", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copiar URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Criar conta" }, + "newToBitwarden": { + "message": "Novo no Bitwarden?" + }, "setAStrongPassword": { "message": "Defina uma palavra-passe forte" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Iniciar sessão" }, + "logInToBitwarden": { + "message": "Iniciar sessão no Bitwarden" + }, + "logInWithPasskey": { + "message": "Iniciar sessão com a chave de acesso" + }, + "loginWithDevice": { + "message": "Iniciar sessão com o dispositivo" + }, + "useSingleSignOn": { + "message": "Utilizar início de sessão único" + }, "submit": { "message": "Submeter" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL do servidor" }, + "selfHostBaseUrl": { + "message": "URL do servidor auto-hospedado", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do servidor da API" }, @@ -1218,6 +1247,9 @@ "message": "Copiar número", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copiar e-mail" + }, "copySecurityCode": { "message": "Copiar código de segurança", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Gerar nome de utilizador" }, + "generateEmail": { + "message": "Gerar e-mail" + }, "usernameType": { "message": "Tipo de nome de utilizador" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Gerar um alias de e-mail com um serviço de reencaminhamento externo." }, + "forwarderDomainName": { + "message": "Domínio de e-mail", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Escolha um domínio que seja suportado pelo serviço selecionado", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro no $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Sends de texto" }, - "allowScreenshots": { - "message": "Permitir a captura de ecrã" - }, - "allowScreenshotsDesc": { - "message": "Permitir a captura de ecrã da aplicação para computador do Bitwarden." - }, "ssoError": { "message": "Não foi possível encontrar portas livres para o início de sessão sso." }, diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index a5e6ccecd1d..f0b7205392d 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Alegeți o organizație la care doriți să mutați acest articol. Mutarea într-o organizație, transferă proprietatea asupra articolului către organizația respectivă. Nu veți mai fi proprietarul direct al acestui articol odată ce a fost mutat." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generare parolă" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tip" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copiere parolă" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copiere URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Creare cont" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Conectare" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Trimitere" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL server API" }, @@ -1218,6 +1247,9 @@ "message": "Copiere număr", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copiere cod de securitate", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generare nume de utilizator" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip de nume de utilizator" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generați un alias de e-mail cu un serviciu de redirecționare extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 24a291935e1..b18f5eaadc4 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Выберите организацию, в которую вы хотите переместить этот элемент. При перемещении в организацию право собственности на элемент переходит к этой организации. Вы больше не будете прямым владельцем этого элемента после его перемещения." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Сгенерировать пароль" }, + "generatePassphrase": { + "message": "Создать парольную фразу" + }, "type": { "message": "Тип" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Скопировать пароль" }, + "copyPassphrase": { + "message": "Скопировать парольную фразу", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Скопировать URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Создать аккаунт" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Задайте надежный пароль" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Войти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Отправить" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL сервера" }, + "selfHostBaseUrl": { + "message": "URL собственного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API сервера" }, @@ -1218,6 +1247,9 @@ "message": "Скопировать номер", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Скопировать email" + }, "copySecurityCode": { "message": "Скопировать код безопасности", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Создать имя пользователя" }, + "generateEmail": { + "message": "Сгенерировать email" + }, "usernameType": { "message": "Тип имени пользователя" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Создать псевдоним электронной почты для внешней службы пересылки." }, + "forwarderDomainName": { + "message": "Домен электронной почты", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Выберите домен, который поддерживается выбранным сервисом", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Ошибка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Текстовая Send" }, - "allowScreenshots": { - "message": "Разрешить скриншоты" - }, - "allowScreenshotsDesc": { - "message": "Разрешить скриншоты в Bitwarden для компьютера." - }, "ssoError": { "message": "Не удалось найти свободные порты для авторизации SSO." }, diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index b6e9a97cc6d..b111d3a5e96 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "මුරපදය පිටපත් කරන්න" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 122a6b51f7f..af6b765d67a 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Vitajte späť" + }, "moveToOrgDesc": { "message": "Vyberte organizáciu, do ktorej chcete presunúť túto položku. Presunom do organizácie sa vlastníctvo položky prenáša na túto organizáciu. Po presunutí už nebudete priamym vlastníkom danej položky." }, @@ -101,7 +104,7 @@ "message": "Upraviť položku" }, "emailAddress": { - "message": "Emailová adresa" + "message": "E-mailová adresa" }, "verificationCodeTotp": { "message": "Overovací kód (TOTP)" @@ -166,7 +169,7 @@ "message": "Číslo vodičského preukazu" }, "email": { - "message": "Email" + "message": "E-mail" }, "phone": { "message": "Telefón" @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generovať heslo" }, + "generatePassphrase": { + "message": "Generovať prístupovú frázu" + }, "type": { "message": "Typ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Skopírovať heslo" }, + "copyPassphrase": { + "message": "Kopírovať prístupovú frázu", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopírovať URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Vytvoriť účet" }, + "newToBitwarden": { + "message": "Ste noví na Bitwardene?" + }, "setAStrongPassword": { "message": "Nastavte silné heslo" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Prihlásiť sa" }, + "logInToBitwarden": { + "message": "Prihlásenie do Bitwardenu" + }, + "logInWithPasskey": { + "message": "Prihlásiť sa s prístupovým kľúčom" + }, + "loginWithDevice": { + "message": "Prihlásiť sa pomocou zariadenia" + }, + "useSingleSignOn": { + "message": "Použiť jednotné prihlásenie" + }, "submit": { "message": "Potvrdiť" }, @@ -633,16 +658,16 @@ "message": "Nápoveď k heslu" }, "enterEmailToGetHint": { - "message": "Zadajte emailovú adresu na zaslanie nápovede k vášmu hlavnému heslu." + "message": "Zadajte e-mailovú adresu na zaslanie nápovede k vášmu hlavnému heslu." }, "getMasterPasswordHint": { "message": "Získať nápoveď k hlavnému heslu" }, "emailRequired": { - "message": "Emailová adresa je povinná." + "message": "E-mailová adresa je povinná." }, "invalidEmail": { - "message": "Neplatná emailová adresa." + "message": "Neplatná e-mailová adresa." }, "masterPasswordRequired": { "message": "Hlavné heslo je povinné." @@ -679,7 +704,7 @@ "message": "Boli ste prihlásený!" }, "masterPassSent": { - "message": "Emailom sme vám poslali nápoveď k hlavnému heslu." + "message": "E-mailom sme vám poslali nápoveď k hlavnému heslu." }, "unexpectedError": { "message": "Vyskytla sa neočakávaná chyba." @@ -721,7 +746,7 @@ "message": "Zadajte 6-miestny overovací kód z vašej overovacej aplikácie." }, "enterVerificationCodeEmail": { - "message": "Zadajte 6-miestny overovací kód, ktorý bol zaslaný emailom na $EMAIL$.", + "message": "Zadajte 6-miestny overovací kód, ktorý bol zaslaný e-mailom na $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -730,7 +755,7 @@ } }, "verificationCodeEmailSent": { - "message": "Overovací email odoslaný na $EMAIL$.", + "message": "Overovací e-mail odoslaný na $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -742,7 +767,7 @@ "message": "Zapamätaj si ma" }, "sendVerificationCodeEmailAgain": { - "message": "Znovu zaslať overovací kód emailom" + "message": "Znovu zaslať overovací kód e-mailom" }, "useAnotherTwoStepMethod": { "message": "Použiť inú dvojstupňovú metódu prihlásenia" @@ -787,7 +812,7 @@ "message": "Použiť akýkoľvek WebAuthn bezpečnostný kľúč pre prístup k vášmu účtu." }, "emailTitle": { - "message": "Email" + "message": "E-mail" }, "emailDescV2": { "message": "Zadajte kód zaslaný na váš e-mail." @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL servera" }, + "selfHostBaseUrl": { + "message": "Adresa URL vlastného hostingu", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API servera" }, @@ -1218,6 +1247,9 @@ "message": "Kopírovať číslo", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Kopírovať e-mail" + }, "copySecurityCode": { "message": "Kopírovať bezpečnostný kód", "description": "Copy credit card security code (CVV)" @@ -1543,7 +1575,7 @@ "message": "Potvrdiť export trezoru" }, "exportWarningDesc": { - "message": "Tento export obsahuje vaše dáta v nešifrovanom formáte. Nemali by ste ich ukladať, ani posielať cez nezabezpečené kanály (napr. email). Okamžite ho odstráňte, keď ho prestanete používať." + "message": "Tento export obsahuje vaše dáta v nešifrovanom formáte. Nemali by ste ich ukladať, ani posielať cez nezabezpečené kanály (napr. e-mail). Okamžite ho odstráňte, keď ho prestanete používať." }, "encExportKeyWarningDesc": { "message": "Tento export zašifruje vaše údaje pomocou šifrovacieho kľúča vášho účtu. Ak niekedy budete rotovať šifrovací kľúč svojho účtu, mali by ste exportovať znova, pretože nebudete môcť dešifrovať tento exportovaný súbor." @@ -2100,7 +2132,7 @@ "message": "Overiť cez WebAuthn" }, "hideEmail": { - "message": "Skryť moju emailovú adresu pred príjemcami." + "message": "Skryť moju e-mailovú adresu pred príjemcami." }, "sendOptionsPolicyInEffect": { "message": "Jedno alebo viac pravidiel organizácie ovplyvňujú vaše možnosti funkcie Send." @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Vygenerovať používateľské meno" }, + "generateEmail": { + "message": "Generovať e-mail" + }, "usernameType": { "message": "Typ používateľského mena" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Vytvoriť e-mailový alias pomocou externej služby preposielania." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, ktorá je podporovaná vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ chyba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -2660,7 +2703,7 @@ "message": "a pokračujte vo vytváraní účtu." }, "noEmail": { - "message": "Žiadny email?" + "message": "Žiadny e-mail?" }, "goBack": { "message": "Prejsť späť" @@ -3162,12 +3205,6 @@ "textSends": { "message": "Textové Sendy" }, - "allowScreenshots": { - "message": "Povoliť snímanie obrazovky" - }, - "allowScreenshotsDesc": { - "message": "Povoliť snímanie obsahu aplikácie Bitwarden Desktop." - }, "ssoError": { "message": "Pre prihlásenie SSO sa nepodarilo nájsť žiadne voľné porty." }, diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 6fcfa04b89d..902b7a0ae36 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generiraj geslo" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tip" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiraj geslo" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiraj URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Ustvari račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Prijava" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Potrdi" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL naslov strežnika" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API strežnika" }, @@ -1218,6 +1247,9 @@ "message": "Kopiraj številko", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopiraj varnostno kodo", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index c5492d1ba60..5409b51e989 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Изаберите организацију у коју желите да преместите ову ставку. Пребацивање у организацију преноси власништво над ставком тој организацији. Више нећете бити директни власник ове ставке када буде премештена." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Генерисање лозинке" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Тип" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Копирај лозинку" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Копирај УРЛ" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Креирај налог" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Поставите јаку лозинку" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Пријавите се" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Пошаљи" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "УРЛ Сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "УРЛ АПИ Сервера" }, @@ -1218,6 +1247,9 @@ "message": "Копирај број", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Копирај сигурносни код", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Генериши име" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тип имена" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ грешка: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Текст „Send“" }, - "allowScreenshots": { - "message": "Дозволи снимање екрана" - }, - "allowScreenshotsDesc": { - "message": "Дозволи снимање екрана Bitwarden десктоп апликације." - }, "ssoError": { "message": "Нису пронађени портови за SSO пријаву." }, diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 01c268c9a94..6818ea09fc0 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Välj en organisation som du vill flytta detta objektet till. Flytt till en organisation överför ägandet av objektet till den organisationen. Du kommer inte längre att vara direkt ägare till detta objekt när det har flyttats." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generera lösenord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Typ" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Kopiera lösenord" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Kopiera URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Skapa konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Ställ in ett starkt lösenord" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Logga in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Skicka" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-server-URL" }, @@ -1218,6 +1247,9 @@ "message": "Kopiera nummer", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Kopiera säkerhetskod", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generera användarnamn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Typ av användarnamn" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Skapa ett e-postalias med en extern vidarebefordranstjänst." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fel: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 9924a91fa36..64109a052c6 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Type" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Copy URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Submit" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -1218,6 +1247,9 @@ "message": "Copy number", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Copy security code", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 0122ad029e9..d0d1a177301 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "เลือกองค์กรที่คุณต้องการย้ายรายการนี้ไป การย้ายไปยังองค์กรจะเป็นการโอนความเป็นเจ้าของรายการไปยังองค์กรนั้น คุณจะไม่ได้เป็นเจ้าของโดยตรงของรายการนี้อีกต่อไปเมื่อมีการย้าย" }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "สร้างรหัสผ่าน" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "ประเภท" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "คัดลอกรหัสผ่าน" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "คัดลอก URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "สร้างบัญชี" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -556,6 +569,18 @@ "logIn": { "message": "เข้าสู่ระบบ" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "ส่งข้อมูล" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL ของเซิร์ฟเวอร์" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -1218,6 +1247,9 @@ "message": "คัดลอกหมายเลข", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "คัดลอกรหัสรักษาความปลอดภัย", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index d163763e5a6..41b3064eefc 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Tekrar hoş geldiniz" + }, "moveToOrgDesc": { "message": "Bu kaydı taşımak istediğiniz kuruluşu seçin. Taşıdığınız kaydın sahipliği seçtiğiniz kuruluşa aktarılacak. Artık bu kaydın doğrudan sahibi olmayacaksınız." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Parola oluştur" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Tür" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Parolayı kopyala" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "URI'yi kopyala" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Hesap oluştur" }, + "newToBitwarden": { + "message": "Bitwarden'da yeni misiniz?" + }, "setAStrongPassword": { "message": "Güçlü bir parola belirleyin" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Giriş yap" }, + "logInToBitwarden": { + "message": "Bitwarden'a giriş yapın" + }, + "logInWithPasskey": { + "message": "Geçiş anahtarıyla giriş yap" + }, + "loginWithDevice": { + "message": "Cihazla giriş yap" + }, + "useSingleSignOn": { + "message": "Çoklu oturum açma kullan" + }, "submit": { "message": "Gönder" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Sunucu URL'si" }, + "selfHostBaseUrl": { + "message": "Kendi kendine barındırılan sunucu URL'si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API sunucu URL'si" }, @@ -1218,6 +1247,9 @@ "message": "Numarayı kopyala", "description": "Copy credit card number" }, + "copyEmail": { + "message": "E-postayı kopyala" + }, "copySecurityCode": { "message": "Güvenlik kodunu kopyala", "description": "Copy credit card security code (CVV)" @@ -1748,7 +1780,7 @@ "message": "Zaman aşımı eylem onayı" }, "enterpriseSingleSignOn": { - "message": "Kurumsal tek oturum açma" + "message": "Kurumsal çoklu oturum açma" }, "setMasterPassword": { "message": "Ana parolayı belirle" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Kullanıcı adı oluştur" }, + "generateEmail": { + "message": "E-posta oluştur" + }, "usernameType": { "message": "Kullanıcı adı türü" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Harici bir yönlendirme servisiyle e-posta maskesi oluştur." }, + "forwarderDomainName": { + "message": "E-posta alan adı", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçtiğiniz servisin desteklediği bir alan adı seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hatası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Metin Send'leri" }, - "allowScreenshots": { - "message": "Ekran kaydına izin ver" - }, - "allowScreenshotsDesc": { - "message": "Bitwarden masaüstü uygulamasının ekran kaydının alınmasına izin ver." - }, "ssoError": { "message": "SSO girişi için açık port bulunamadı." }, diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index d4b9804e418..63e2f00ed02 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Виберіть організацію, до якої ви бажаєте перемістити цей запис. Переміщуючи до організації, власність запису передається тій організації. Ви більше не будете єдиним власником цього запису після переміщення." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Генерувати пароль" }, + "generatePassphrase": { + "message": "Генерувати парольну фразу" + }, "type": { "message": "Тип" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Копіювати пароль" }, + "copyPassphrase": { + "message": "Копіювати парольну фразу", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Копіювати URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Створити обліковий запис" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Встановіть надійний пароль" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Увійти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Відправити" }, @@ -603,7 +628,7 @@ "message": "Приєднатися до організації" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Приєднатися до $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -828,6 +853,10 @@ "baseUrl": { "message": "URL-адреса сервера" }, + "selfHostBaseUrl": { + "message": "URL-адреса власного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL-адреса сервера API" }, @@ -1218,6 +1247,9 @@ "message": "Копіювати номер", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Копіювати е-пошту" + }, "copySecurityCode": { "message": "Копіювати код безпеки", "description": "Copy credit card security code (CVV)" @@ -1762,7 +1794,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "з $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Генерувати ім'я користувача" }, + "generateEmail": { + "message": "Генерувати е-пошту" + }, "usernameType": { "message": "Тип імені користувача" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Згенеруйте псевдонім е-пошти зі стороннім сервісом пересилання." }, + "forwarderDomainName": { + "message": "Домен електронної пошти", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Виберіть домен, який підтримується вибраною службою", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Помилка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Відправлення тексту" }, - "allowScreenshots": { - "message": "Дозволити захоплення екрана" - }, - "allowScreenshotsDesc": { - "message": "Дозволити захоплення екрана для комп'ютерної програми Bitwarden." - }, "ssoError": { "message": "Не знайдено вільних портів для цього входу SSO." }, diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 35619358d9d..e79a66153f6 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "Chọn một tổ chức mà bạn muốn chuyển mục này đến. Việc chuyển đến một tổ chức sẽ chuyển quyền sở hữu mục này cho tổ chức đó. Bạn sẽ không còn là chủ sở hữu trực tiếp của mục này khi đã chuyển." }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "Tạo mật khẩu" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "Loại" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "Sao chép Mật khẩu" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "Sao chép URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "Tạo tài khoản" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "Đặt mật khẩu mạnh" }, @@ -556,6 +569,18 @@ "logIn": { "message": "Đăng Nhập" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "Gửi" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "Địa chỉ máy chủ" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Địa chỉ API máy chủ" }, @@ -1218,6 +1247,9 @@ "message": "Chép số", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "Sao chép Mã bảo mật", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "Tạo tên tài khoản" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Loại tên người dùng" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "Tạo bí danh email với dịch vụ chuyển tiếp bên ngoài." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Lỗi $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Gửi tin nhắn" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "Không thể tìm thấy cổng trống để đăng nhập SSO." }, diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index a3dbb079521..bd07219f84d 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "选择一个您想将此项目移至的组织。移动到组织会将该项目的所有权转让给该组织。移动后,您将不再是此项目的直接所有者。" }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "生成密码" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "类型" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "复制密码" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "复制 URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "创建账户" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "设置强密码" }, @@ -556,6 +569,18 @@ "logIn": { "message": "登录" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "提交" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "服务器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 服务器 URL" }, @@ -1218,6 +1247,9 @@ "message": "复制号码", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "复制安全码", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "生成用户名" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "用户名类型" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "使用外部转发服务生成一个电子邮件别名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ 错误:$ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "文本 Send" }, - "allowScreenshots": { - "message": "允许截屏" - }, - "allowScreenshotsDesc": { - "message": "允许对 Bitwarden 桌面应用程序截屏。" - }, "ssoError": { "message": "找不到用于 SSO 登录的可用端口。" }, diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 3ae3df3762a..d3519833349 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -60,6 +60,9 @@ } } }, + "welcomeBack": { + "message": "Welcome back" + }, "moveToOrgDesc": { "message": "選擇您希望將這個項目移動到哪個組織。項目的擁有權將會轉移到該組織。一經移動,您將不再是此項目的直接擁有者。" }, @@ -259,6 +262,9 @@ "generatePassword": { "message": "產生密碼" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "type": { "message": "類型" }, @@ -394,6 +400,10 @@ "copyPassword": { "message": "複製密碼" }, + "copyPassphrase": { + "message": "Copy passphrase", + "description": "Copy passphrase to clipboard" + }, "copyUri": { "message": "複製 URI" }, @@ -547,6 +557,9 @@ "createAccount": { "message": "建立帳戶" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, "setAStrongPassword": { "message": "設定一個強密碼" }, @@ -556,6 +569,18 @@ "logIn": { "message": "登入" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithDevice": { + "message": "Log in with device" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, "submit": { "message": "送出" }, @@ -828,6 +853,10 @@ "baseUrl": { "message": "伺服器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 伺服器 URL" }, @@ -1218,6 +1247,9 @@ "message": "複製號碼", "description": "Copy credit card number" }, + "copyEmail": { + "message": "Copy email" + }, "copySecurityCode": { "message": "複製安全代碼", "description": "Copy credit card security code (CVV)" @@ -2352,6 +2384,9 @@ "generateUsername": { "message": "產生使用者名稱" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "使用者名稱類型" }, @@ -2395,6 +2430,14 @@ "forwardedEmailDesc": { "message": "使用外部轉寄服務產生一個電子郵件別名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -3162,12 +3205,6 @@ "textSends": { "message": "Text Sends" }, - "allowScreenshots": { - "message": "Allow screen capture" - }, - "allowScreenshotsDesc": { - "message": "Allow screen capture of the Bitwarden desktop application." - }, "ssoError": { "message": "No free ports could be found for the sso login." }, From 599f9ea549095edcc47d0315e22b606991e50625 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:47:50 +0000 Subject: [PATCH 078/126] Autosync the updated translations (#11708) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/bg/messages.json | 10 +++++----- apps/web/src/locales/fr/messages.json | 10 +++++----- apps/web/src/locales/hu/messages.json | 14 +++++++------- apps/web/src/locales/lv/messages.json | 10 +++++----- apps/web/src/locales/sk/messages.json | 10 +++++----- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 98415770179..a251cdffa12 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -940,13 +940,13 @@ "message": "Използване на друг метод на вписване" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Вписване със секретен ключ" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Използване на еднократна идентификация" }, "welcomeBack": { - "message": "Welcome back" + "message": "Добре дошли отново" }, "invalidPasskeyPleaseTryAgain": { "message": "Грешен секретен ключ. Опитайте отново." @@ -1030,7 +1030,7 @@ "message": "Създаване на абонамент" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "За пръв път ли ползвате Битуорден?" }, "setAStrongPassword": { "message": "Използвайте сложна парола" @@ -1048,7 +1048,7 @@ "message": "Вписване" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Впишете се в Битуорден" }, "verifyIdentity": { "message": "Потвърдете самоличността си" diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 38890b3c1fb..780caf551d5 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -940,13 +940,13 @@ "message": "Utiliser une méthode de connexion différente" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Se connecter avec une clé d'accès" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Utiliser l'authentification unique" }, "welcomeBack": { - "message": "Welcome back" + "message": "Bon retour" }, "invalidPasskeyPleaseTryAgain": { "message": "Passkey invalide. Veuillez réessayer de nouveau." @@ -1030,7 +1030,7 @@ "message": "Créez un compte" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Nouveau sur Bitwarden?" }, "setAStrongPassword": { "message": "Définir un mot de passe fort" @@ -1048,7 +1048,7 @@ "message": "Se connecter" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Se connecter à Bitwarden" }, "verifyIdentity": { "message": "Vérifiez votre Identité" diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 2c5f9daabbc..14b43174715 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -940,13 +940,13 @@ "message": "Más bejelentkezési mód használata" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Bejelentkezés hozzáférési kulccsal" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Egyszeri bejelentkezés használata" }, "welcomeBack": { - "message": "Welcome back" + "message": "Üdvözlet újra" }, "invalidPasskeyPleaseTryAgain": { "message": "Érvénytelen jelszó. Próbálja újra." @@ -1000,7 +1000,7 @@ "message": "Használat titkosításhoz" }, "loginWithPasskeyEnabled": { - "message": "Jelentkezzünk be bekapcsolt jelszóval" + "message": "Az azonosító kulcssal bejelentkezés bekapcsolásra került." }, "passkeySaved": { "message": "$NAME$ mentésre került.", @@ -1030,7 +1030,7 @@ "message": "Fiók létrehozása" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Új vagyunk a Bitwardenben?" }, "setAStrongPassword": { "message": "Erős jelszó beállítása" @@ -1048,7 +1048,7 @@ "message": "Bejelentkezés" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Bejelentkezés a Bitwardenbe" }, "verifyIdentity": { "message": "Személyazonosság ellenőrzése" @@ -1336,7 +1336,7 @@ "message": "FIDO U2F Biztonsági kulcs" }, "webAuthnTitle": { - "message": "FIDO2 WebAuthn" + "message": "Biztonsági kulcs" }, "webAuthnDesc": { "message": "Használjunk bármilyen WebAuthn engedélyezett biztonsági kulcsot a saját fiók eléréséhez." diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 7d614ff867a..efa48979ff9 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -940,13 +940,13 @@ "message": "Jāizmanto cits pieteikšanās veids" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Pieteikties ar piekļuves atslēgu" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Izmantot vienoto pieteikšanos" }, "welcomeBack": { - "message": "Welcome back" + "message": "Laipni lūdzam atpakaļ" }, "invalidPasskeyPleaseTryAgain": { "message": "Nederīga piekļuves atslēga. Lūgums mēģināt vēlreiz." @@ -1030,7 +1030,7 @@ "message": "Izveidot kontu" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Bitwarden iepriekš nav izmantots?" }, "setAStrongPassword": { "message": "Jāiestata droša parole" @@ -1048,7 +1048,7 @@ "message": "Pieteikties" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Pieteikties Bitwarden" }, "verifyIdentity": { "message": "Jāapliecina sava identitāte" diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index ea198f5d8f4..1446a292374 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -940,13 +940,13 @@ "message": "Použiť iný spôsob prihlásenia" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Prihlásiť sa s prístupovým kľúčom" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Použiť jednotné prihlásenie" }, "welcomeBack": { - "message": "Welcome back" + "message": "Vitajte späť" }, "invalidPasskeyPleaseTryAgain": { "message": "Nesprávne Passkey heslo. Prosím skúste to znovu." @@ -1030,7 +1030,7 @@ "message": "Vytvoriť účet" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Ste noví na Bitwardene?" }, "setAStrongPassword": { "message": "Nastavte silné heslo" @@ -1048,7 +1048,7 @@ "message": "Prihlásiť sa" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Prihlásenie do Bitwardenu" }, "verifyIdentity": { "message": "Overte svoju totožnosť" From 619618e6f37d205c3d2639ddc21ae1220395695b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:32:16 +0000 Subject: [PATCH 079/126] Autosync the updated translations (#11702) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/az/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/be/messages.json | 244 ++++++++++++++++-- apps/browser/src/_locales/bg/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/bn/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/bs/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ca/messages.json | 232 ++++++++++++++++- apps/browser/src/_locales/cs/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/cy/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/da/messages.json | 224 +++++++++++++++- apps/browser/src/_locales/de/messages.json | 216 +++++++++++++++- apps/browser/src/_locales/el/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/en_GB/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/en_IN/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/es/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/et/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/eu/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/fa/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/fi/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/fil/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/fr/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/gl/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/he/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/hi/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/hr/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/hu/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/id/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/it/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ja/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ka/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/km/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/kn/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ko/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/lt/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/lv/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ml/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/mr/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/my/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/nb/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ne/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/nl/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/nn/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/or/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/pl/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/pt_BR/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/pt_PT/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/ro/messages.json | 232 ++++++++++++++++- apps/browser/src/_locales/ru/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/si/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/sk/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/sl/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/sr/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/sv/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/te/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/th/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/tr/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/uk/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/vi/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/zh_CN/messages.json | 212 ++++++++++++++- apps/browser/src/_locales/zh_TW/messages.json | 212 ++++++++++++++- apps/browser/store/locales/be/copy.resx | 2 +- 61 files changed, 12645 insertions(+), 165 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index ff9fa87252e..83ea8f797e9 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "إنشاء حساب" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "نسخ كلمة المرور" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "نسخ الملاحظة" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "التعبئة التلقائية" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "توليد كلمة مرور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "إعادة توليد كلمة المرور" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "الموقع الإلكتروني" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "رابط الخادم" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "رابط خادم API" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "إنشاء اسم المستخدم" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع اسم المستخدم" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "إنشاء بريد إلكتروني مستعار مع خدمة إعادة توجيه خارجية." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index ca4f3e5a0ef..930624e723d 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Hesab yarat" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Güclü bir parol təyin et" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Parolu kopyala" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Notu kopyala" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notları kopyala" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Avto-doldurma" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Parol yarat" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Parolu yenidən yarat" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Veb saytı başlat" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Veb sayt" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Giriş et" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Qeydiyyatı yenidən başlat" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL-si" }, + "selfHostBaseUrl": { + "message": "Self-host server URL-si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL-si" }, @@ -2464,8 +2502,8 @@ "message": "İstəyinizə görə istifadəçilərdən bu \"Send\"ə müraciət edərkən parol tələb edə bilərsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Send-ə baxmaq üçün bu parol tələb edilsin.", + "sendPasswordDescV3": { + "message": "Alıcıların bu \"Send\"ə müraciət etməsi üçün ixtiyari bir parol əlavə edin.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "İstifadəçi adı yarat" }, + "generateEmail": { + "message": "E-poçt yarat" + }, "usernameType": { "message": "İstifadəçi adı növü" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Xarici yönləndirmə xidməti ilə e-poçt ləqəbi yaradın." }, + "forwarderDomainName": { + "message": "E-poçt domeni", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçilmiş xidmət tərəfindən dəstəklənən bir domen seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ xətası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Element yeri" }, + "fileSend": { + "message": "Fayl \"Send\"i" + }, "fileSends": { "message": "Fayl \"Send\"ləri" }, + "textSend": { + "message": "Mətn \"Send\"i" + }, "textSends": { "message": "Mətn \"Send\"ləri" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Kimlik doğrulama" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 4ce7691c943..690278de85c 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden Password Manager", + "message": "Менеджар пароляў Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -14,11 +14,23 @@ "message": "Увайдзіце або стварыце новы ўліковы запіс для доступу да бяспечнага сховішча." }, "inviteAccepted": { - "message": "Invitation accepted" + "message": "Запрашэнне прынята" }, "createAccount": { "message": "Стварыць уліковы запіс" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Скапіяваць пароль" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Скапіяваць нататку" }, @@ -123,7 +138,7 @@ "message": "Скапіяваць код бяспекі" }, "copyName": { - "message": "Copy name" + "message": "Скапіяваць імя" }, "copyCompany": { "message": "Copy company" @@ -150,7 +165,11 @@ "message": "Copy website" }, "copyNotes": { - "message": "Copy notes" + "message": "Скапіяваць нататкі" + }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { "message": "Аўтазапаўненне" @@ -281,13 +300,13 @@ "message": "Выйсці" }, "aboutBitwarden": { - "message": "About Bitwarden" + "message": "Пра Bitwarden" }, "about": { "message": "Пра Bitwarden" }, "moreFromBitwarden": { - "message": "More from Bitwarden" + "message": "Больш ад Bitwarden" }, "continueToBitwardenDotCom": { "message": "Continue to bitwarden.com?" @@ -338,10 +357,10 @@ "message": "Рэдагаваць папку" }, "newFolder": { - "message": "New folder" + "message": "Новая папка" }, "folderName": { - "message": "Folder name" + "message": "Назва папкі" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -396,7 +415,7 @@ "message": "Аўтаматычна генерыруйце надзейныя і ўнікальныя паролі для вашых лагінаў." }, "bitWebVaultApp": { - "message": "Bitwarden web app" + "message": "Вэб-праграма Bitwarden" }, "importItems": { "message": "Імпартаванне элементаў" @@ -407,6 +426,9 @@ "generatePassword": { "message": "Генерыраваць пароль" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Паўторна генерыраваць пароль" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Вэб-сайт" }, @@ -581,7 +612,7 @@ "message": "Iншае" }, "unlockMethods": { - "message": "Unlock options" + "message": "Параметры блакіроўкі" }, "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Наладзіць метад разблакіроўкі для змянення дзеяння часу чакання вашага сховішча." @@ -620,7 +651,7 @@ "message": "Your account is locked" }, "or": { - "message": "or" + "message": "або" }, "unlock": { "message": "Разблакіраваць" @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -894,7 +928,7 @@ "message": "Новы URI" }, "addDomain": { - "message": "Add domain", + "message": "Дадаць дамен", "description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context." }, "addedItem": { @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL-адрас сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Сервер URL-адраса API" }, @@ -2464,8 +2502,8 @@ "message": "Па магчымасці запытваць у карыстальнікаў пароль для доступу да гэтага Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генерыраваць імя карыстальніка" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тып імя карыстальніка" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Генерыраваць псеўданім электроннай пошты са знешнім сэрвісам перасылкі." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4036,13 +4085,13 @@ "message": "Admin Console" }, "accountSecurity": { - "message": "Account security" + "message": "Бяспеке акаўнта" }, "notifications": { - "message": "Notifications" + "message": "Апавяшчэнні" }, "appearance": { - "message": "Appearance" + "message": "Знешні выгляд" }, "errorAssigningTargetCollection": { "message": "Error assigning target collection." @@ -4274,7 +4323,7 @@ } }, "enableAnimations": { - "message": "Enable animations" + "message": "Уключыць анімацыі" }, "showAnimations": { "message": "Show animations" @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 6165f4832ed..d0646bbf513 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Създаване на акаунт" }, + "newToBitwarden": { + "message": "За пръв път ли ползвате Битуорден?" + }, + "logInWithPasskey": { + "message": "Вписване със секретен ключ" + }, + "useSingleSignOn": { + "message": "Използване на еднократна идентификация" + }, + "welcomeBack": { + "message": "Добре дошли отново" + }, "setAStrongPassword": { "message": "Използвайте сложна парола" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Копиране на паролата" }, + "copyPassphrase": { + "message": "Копиране на паролата-фраза" + }, "copyNote": { "message": "Копиране на бележката" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Копиране на бележките" }, + "fill": { + "message": "Попълване", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Автоматично дописване" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Нова парола" }, + "generatePassphrase": { + "message": "Генериране на парола-фраза" + }, "regeneratePassword": { "message": "Регенериране на паролата" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Посещаване на уеб сайта" }, + "launchWebsiteName": { + "message": "Отваряне на уеб сайта $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Сайт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Вписване" }, + "logInToBitwarden": { + "message": "Впишете се в Битуорден" + }, "restartRegistration": { "message": "Рестартиране на регистрацията" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Адрес на сървъра" }, + "selfHostBaseUrl": { + "message": "Адрес на собствения сървър", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Адрес на ППИ-сървъра" }, @@ -2464,8 +2502,8 @@ "message": "Изискване на парола за достъп до това изпращане.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Изискване на тази парола за преглеждане на Изпращането.", + "sendPasswordDescV3": { + "message": "Добавете незадължителна парола, с която получателите да имат достъп до това Изпращане.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генериране на потр. име" }, + "generateEmail": { + "message": "Генериране на електронна поща" + }, "usernameType": { "message": "Тип потребителско име" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Създайте псевдоним на е-поща с външна услуга за препращане." }, + "forwarderDomainName": { + "message": "Домейн на електронната поща", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Изберете домейн, който се поддържа от избраната услуга", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Грешка от $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Местоположение на елемента" }, + "fileSend": { + "message": "Файлово изпращане" + }, "fileSends": { "message": "Файлови изпращания" }, + "textSend": { + "message": "Текстово изпращане" + }, "textSends": { "message": "Текстови изпращания" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Удостоверяване" + }, + "fillGeneratedPassword": { + "message": "Попълване на генерираната парола", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Паролата е прегенерирана", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Запазване на данните за вписване в Битуорден?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Интервал", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Вълничка", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Удивителен знак", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Кльомба", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Диез", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Знак за долар", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Процент", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Колибка", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Амперсанд", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Звездичка", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Лява скоба", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Дясна скоба", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Долна черта", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Тире", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Плюс", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Равно", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Лява фигурна скоба", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Дясна фигурна скоба", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Лява квадратна скоба", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Дясна квадратна скоба", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Вертикална черта", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Обратна наклонена черта", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Двоеточие", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Точка и запетая", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Двойна кавичка", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Единична кавичка", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "По-малко", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "По-голямо", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Запетая", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Точка", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Въпросителен знак", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Наклонена черта", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Малки букви" + }, + "uppercaseAriaLabel": { + "message": "Главни букви" + }, + "generatedPassword": { + "message": "Генерирана парола" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 9c6f2a5a99e..c694b3b5724 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "অ্যাকাউন্ট তৈরি করুন" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "পাসওয়ার্ড অনুলিপিত করুন" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "নোট অনুলিপিত করুন" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "স্বতঃপূরণ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "পাসওয়ার্ড তৈরি করুন" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "পাসওয়ার্ড পুনঃতৈরি করুন" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ওয়েবসাইট" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "সার্ভার URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "এপিআই সার্ভার URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 95f90988df4..0702287b27e 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Napravi račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index ec0fd8bef7c..6829e84ba3d 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crea un compte" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Estableix una contrasenya segura" }, @@ -72,7 +84,7 @@ "message": "Uneix-te a l'organització" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Uniu-vos a $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copia contrasenya" }, + "copyPassphrase": { + "message": "Copia clau de pas" + }, "copyNote": { "message": "Copia nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copia notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Emplenament automàtic" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Genera contrasenya" }, + "generatePassphrase": { + "message": "Genera clau de pas" + }, "regeneratePassword": { "message": "Regenera contrasenya" }, @@ -440,7 +462,7 @@ "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "Inclou majúscules", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -448,7 +470,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "Inclou minúscules", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -456,7 +478,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Inclou números", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -464,7 +486,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Inclou caràcters especials", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -495,7 +517,7 @@ "description": "deprecated. Use avoidAmbiguous instead." }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "Eviteu caràcters ambigus", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Obri la web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Lloc web" }, @@ -593,7 +624,7 @@ "message": "Temps de sessió" }, "vaultTimeoutHeader": { - "message": "Vault timeout" + "message": "Temps d'espera de la caixa forta" }, "otherOptions": { "message": "Altres opcions" @@ -614,13 +645,13 @@ "message": "La caixa forta està bloquejada. Comproveu la contrasenya mestra per continuar." }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "La caixa forta està bloquejada" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "El compte està bloquejat" }, "or": { - "message": "or" + "message": "o" }, "unlock": { "message": "Desbloqueja" @@ -814,6 +845,9 @@ "logIn": { "message": "Inicia sessió" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del servidor API" }, @@ -2464,8 +2502,8 @@ "message": "Opcionalment, necessiteu una contrasenya perquè els usuaris accedisquen a aquest Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Genera un nom d'usuari" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipus de nom d'usuari" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genera un àlies de correu electrònic amb un servei de reenviament extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Error de $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index bdc245099c1..3baa325fe8e 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Vytvořit účet" }, + "newToBitwarden": { + "message": "Jste noví na Bitwardenu?" + }, + "logInWithPasskey": { + "message": "Přihlásit se pomocí přístupového klíče" + }, + "useSingleSignOn": { + "message": "Použít jednotné přihlášení" + }, + "welcomeBack": { + "message": "Vítejte zpět" + }, "setAStrongPassword": { "message": "Nastavit hlavní heslo" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopírovat heslo" }, + "copyPassphrase": { + "message": "Kopírovat heslovou frázi" + }, "copyNote": { "message": "Kopírovat poznámku" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopírovat poznámky" }, + "fill": { + "message": "Vyplnit", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatické vyplňování" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Vygenerovat heslo" }, + "generatePassphrase": { + "message": "Vygenerovat heslovou frázi" + }, "regeneratePassword": { "message": "Vygenerovat jiné heslo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Otevřít webovou stránku" }, + "launchWebsiteName": { + "message": "Spustit web $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webová stránka" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Přihlásit se" }, + "logInToBitwarden": { + "message": "Přihlásit se do Bitwardenu" + }, "restartRegistration": { "message": "Restartovat registraci" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL serveru" }, + "selfHostBaseUrl": { + "message": "Adresa URL serveru vlastního hostování", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API serveru" }, @@ -2464,8 +2502,8 @@ "message": "Volitelně vyžadovat heslo pro přístup k tomuto Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Pro zobrazení Send bude vyžadováno toto heslo.", + "sendPasswordDescV3": { + "message": "Přidá volitelné heslo pro příjemce pro přístup k tomuto Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Vygenerovat uživatelské jméno" }, + "generateEmail": { + "message": "Vygenerovat e-mail" + }, "usernameType": { "message": "Typ uživatelského jména" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Vygenerovat e-mailový alias pomocí externí služby pro přesměrování." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, která je podporována vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Chyba $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Umístění položky" }, + "fileSend": { + "message": "Send souboru" + }, "fileSends": { "message": "Sends se soubory" }, + "textSend": { + "message": "Send textu" + }, "textSends": { "message": "Sends s texty" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Ověřování" + }, + "fillGeneratedPassword": { + "message": "Vyplnit vygenerované heslo", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Heslo bylo znovu vygenerováno", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Uložit přihlášení do Bitwardenu?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Mezera", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilda", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Zpětný apostrof", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Vykřičník", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Zavináč", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Mřížka", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolar", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Procento", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Stříška", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Hvězdička", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Levá závorka", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Pravá závorka", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Podtržítko", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Spojovník", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Rovnítko", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Levá složená závorka", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Pravá složená závorka", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Levá hranatá závorka", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Pravá hranatá závorka", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Svislá čára", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Zpětné lomítko", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Dvojtečka", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Středník", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Dvojitá uvozovka", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Apostrof", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Menší než", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Větší než", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Čárka", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Tečka", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Otazník", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Lomítko", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Všechna malá písmena" + }, + "uppercaseAriaLabel": { + "message": "Všechna velká písmena" + }, + "generatedPassword": { + "message": "Vygenerované heslo" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index e5b77b001ec..90b5987a1cb 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Creu cyfrif" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Gosod cyfrinair cryf" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copïo cyfrinair" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copïo'r nodyn" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Llenwi'n awtomatig" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Cynhyrchu cyfrinair" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Ailgynhyrchu cyfrinair" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Gwefan" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Cynhyrchu enw defnyddiwr" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Math o enw defnyddiwr" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 8ec111967fa..d14b62a3dd8 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Opret konto" }, + "newToBitwarden": { + "message": "Ny på Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log ind med adgangsnøgle" + }, + "useSingleSignOn": { + "message": "Brug Single Sign-On" + }, + "welcomeBack": { + "message": "Velkommen tilbage" + }, "setAStrongPassword": { "message": "Indstil en stærk adgangskode" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiér adgangskode" }, + "copyPassphrase": { + "message": "Kopiér adgangssætning" + }, "copyNote": { "message": "Kopiér notat" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiér notater" }, + "fill": { + "message": "Udfyld", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autoudfyld" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generér adgangskode" }, + "generatePassphrase": { + "message": "Generér adgangssætning" + }, "regeneratePassword": { "message": "Regenerér adgangskode" }, @@ -545,7 +567,7 @@ "message": "Notater" }, "privateNote": { - "message": "Private note" + "message": "Privat notat" }, "note": { "message": "Notat" @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Åbn websted" }, + "launchWebsiteName": { + "message": "Åbn webstedet $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Hjemmeside" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log ind" }, + "logInToBitwarden": { + "message": "Log ind på Bitwarden" + }, "restartRegistration": { "message": "Genstart registrering" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "URL til selv-hostet server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-server URL" }, @@ -2302,14 +2340,14 @@ "message": "Ekskluderet domæne-ændringer gemt" }, "limitSendViews": { - "message": "Limit views" + "message": "Begræns visninger" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "Ingen kan se denne Send efter grænsen er nået.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "$ACCESSCOUNT$ visninger tilbage", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2464,8 +2502,8 @@ "message": "Valgfrit brugeradgangskodekrav for at tilgå denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Tilføj en valgfri adgangskode til modtagere for adgang til denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2554,7 +2592,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { - "message": "For at oprette en fil-Send, skal udvidelsen poppes ud til et nyt vindue.", + "message": "For at oprette en fil-Send, skal udvidelsen poppes ud i et nyt vindue.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { @@ -2603,7 +2641,7 @@ "message": "Skjul min e-mailadresse for modtagere." }, "hideYourEmail": { - "message": "Hide your email address from viewers." + "message": "Skjul e-mailadressen for modtagere." }, "sendOptionsPolicyInEffect": { "message": "Én eller flere organisationspolitikker påvirker dine Send-valgmuligheder." @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generér brugernavn" }, + "generateEmail": { + "message": "Generér e-mail" + }, "usernameType": { "message": "Brugernavnstype" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generér et e-mail alias med en ekstern viderestillingstjeneste." }, + "forwarderDomainName": { + "message": "E-maildomæne", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vælg et domæne understøttet af den valgte tjeneste", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fejl: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Emneplacering" }, + "fileSend": { + "message": "Fil Send" + }, "fileSends": { "message": "Fil-Sends" }, + "textSend": { + "message": "Tekst Send" + }, "textSends": { "message": "Tekst-Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Godkender" + }, + "fillGeneratedPassword": { + "message": "Udfyld genereret adgangskode", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Adgangskode genereret igen", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Gem login til Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Mellemrum", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Accent grave", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Udråbstegn", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Snabel-a", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash-tegn", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar-tegn", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Procenttegn", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Cirkumfleks", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Og-tegn", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Stjerne", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Venstre parentes", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Højre parentes", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Understregning", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Bindestreg", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Lighedstegn", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Venstre tuborg", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Højre tuborg", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Venstre klamme", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Højre klamme", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Lodret streg", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Omvendt skråstreg", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Kolon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semikolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Dobbelt anførselstegn", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Enkelt anførselstegn", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Mindre end", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Større end", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Komma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Punktum", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Spørgsmålstegn", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Skråstreg", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Minuskel" + }, + "uppercaseAriaLabel": { + "message": "Majuskel" + }, + "generatedPassword": { + "message": "Genereret adgangskode" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index a28c8f947b6..c0748518f54 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Konto erstellen" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Lege ein starkes Passwort fest" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Passwort kopieren" }, + "copyPassphrase": { + "message": "Passphrase kopieren" + }, "copyNote": { "message": "Notiz kopieren" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notizen kopieren" }, + "fill": { + "message": "Ausfüllen", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-Ausfüllen" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Passwort generieren" }, + "generatePassphrase": { + "message": "Passphrase generieren" + }, "regeneratePassword": { "message": "Passwort neu generieren" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Website öffnen" }, + "launchWebsiteName": { + "message": "Website aufrufen: $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webseite" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Anmelden" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Registrierung neu starten" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Selbst gehostete Server-URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server-URL" }, @@ -1418,10 +1456,10 @@ "message": "Vorschläge zum Auto-Ausfüllen in Formularfeldern anzeigen" }, "showInlineMenuIdentitiesLabel": { - "message": "Zeige Identitäten als Vorschläge" + "message": "Identitäten als Vorschläge anzeigen" }, "showInlineMenuCardsLabel": { - "message": "Zeige Karten als Vorschläge" + "message": "Karten als Vorschläge anzeigen" }, "showInlineMenuOnIconSelectionLabel": { "message": "Vorschläge anzeigen, wenn Symbol ausgewählt ist" @@ -2464,8 +2502,8 @@ "message": "Optional ein Passwort verlangen, damit Benutzer auf dieses Send zugreifen können.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Dieses Passwort zum Anzeigen des Sends verlangen.", + "sendPasswordDescV3": { + "message": "Füge ein optionales Passwort hinzu, mit dem Empfänger auf dieses Send zugreifen können.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Benutzername generieren" }, + "generateEmail": { + "message": "E-Mail generieren" + }, "usernameType": { "message": "Benutzernamenstyp" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generiere ein E-Mail-Alias mit einem externen Weiterleitungsdienst." }, + "forwarderDomainName": { + "message": "E-Mail-Domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Wähle eine Domain aus, die vom ausgewählten Dienst unterstützt wird", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ Fehler: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Eintrags-Standort" }, + "fileSend": { + "message": "Datei-Send" + }, "fileSends": { "message": "Datei-Sends" }, + "textSend": { + "message": "Text-Send" + }, "textSends": { "message": "Text-Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authentifizierung" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 3318e057024..3cbcf58ac7c 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Δημιουργία λογαριασμού" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Ορίστε έναν ισχυρό κωδικό πρόσβασης" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Αντιγραφή κωδικού πρόσβασης" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Αντιγραφή σημείωσης" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Αντιγραφή σημειώσεων" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Αυτόματη συμπλήρωση" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Δημιουργία κωδικού πρόσβασης" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Επαναδημιουργία κωδικού πρόσβασης" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Εκκίνηση ιστοσελίδας" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Ιστοσελίδα" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Σύνδεση" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Επανεκκίνηση εγγραφής" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL Διακομιστή" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL διακομιστή API" }, @@ -2464,8 +2502,8 @@ "message": "Προαιρετικά απαιτείται κωδικός πρόσβασης για τους χρήστες για να έχουν πρόσβαση σε αυτό το Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Να απαιτείται αυτός ο κωδικός πρόσβασης για την προβολή του Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Δημιουργία ονόματος χρήστη" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Τύπος ονόματος χρήστη" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Δημιουργήστε ένα alias email με μια εξωτερική υπηρεσία προώθησης." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ σφάλμα: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Τοποθεσία Αντικειμένου" }, + "fileSend": { + "message": "Send αρχείου" + }, "fileSends": { "message": "Send αρχείων" }, + "textSend": { + "message": "Send κειμένου" + }, "textSends": { "message": "Send κειμένων" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Ταυτοποίηση" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 4d295f1c9c5..0aae94aee47 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-fill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Full stop", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 6104f15f630..4eab6adf74a 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-fill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate Username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username Type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 0eadf338669..28d63aca133 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crear cuenta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Establece una contraseña fuerte" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar contraseña" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiar nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autorrellenar" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generar contraseña" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerar contraseña" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Iniciar página web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Acceder" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Reiniciar registro" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL del servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del servidor de la API" }, @@ -2464,8 +2502,8 @@ "message": "Opcionalmente se requiere una contraseña para que los usuarios accedan a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generar nombre de usuario" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de nombre de usuario" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genera un alias de correo electrónico con un servicio de reenvío externo." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Ubicación del elemento" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 83562adbb75..13b521c47a1 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Konto loomine" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Määra tugev parool" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopeeri parool" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopeeri märkus" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automaatne täitmine" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Loo parool" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Genereeri parool uuesti" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Ava Veebileht" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Veebileht" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Logi sisse" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Alusta registreerimist uuesti" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Serveri URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API serveri URL" }, @@ -2464,8 +2502,8 @@ "message": "Soovi korral nõua parooli, millega Sendile ligi pääseb.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Genereeri kasutajanimi" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Kasutajanime tüüp" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genereeri e-posti alias, kasutades selleks välist teenuspakkujat." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 39c1d323627..037873bbc5f 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Sortu kontua" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiatu pasahitza" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiatu oharra" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-betetzea" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Sortu pasahitza" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Berrezarri pasahitza" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webgunea" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Zerbitzariaren URL-a" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API zerbitzariaren URL-a" }, @@ -2464,8 +2502,8 @@ "message": "Nahi izanez gero, pasahitza eskatu erabiltzaileak Send honetara sar daitezen.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Sortu erabiltzaile izena" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Erabiltzaile izen mota" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Emaileko ezizen bat sortu kanpoko bidalketa zerbitzu batekin." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 3d7ea582616..42005f3f45c 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ایجاد حساب کاربری" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "تنظیم رمز عبور قوی" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "کپی کلمه عبور" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "کپی یادداشت" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "پر کردن خودکار" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "تولید کلمه عبور" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "تولید مجدد کلمه عبور" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "وب‌سایت" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "نشانی اینترنتی سرور" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "نشانی API سرور" }, @@ -2464,8 +2502,8 @@ "message": "به صورت اختیاری برای دسترسی کاربران به این ارسال به یک کلمه عبور نیاز دارید.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "ایجاد نام کاربری" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "نوع نام کاربری" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "یک نام مستعار ایمیل با یک سرویس ارسال خارجی ایجاد کنید." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 6f325a2f6a9..dcd38ff6c7a 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Luo tili" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Aseta vahva salasana" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopioi salasana" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopioi merkinnät" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopioi merkinnät" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automaattitäyttö" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Luo salasana" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Luo uusi salasana" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Avaa verkkosivusto" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Verkkosivusto" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Kirjaudu" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Aloita rekisteröityminen alusta" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Palvelimen URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-palvelimen URL" }, @@ -2464,8 +2502,8 @@ "message": "Halutessasi, vaadi käyttäjiä syöttämään salasana Sendin avaamiseksi.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Vaadi tämä salasana Sendin avaukseen.", + "sendPasswordDescV3": { + "message": "Lisää valinnainen salasana vastaanottajille tähän Sendiin.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Luo käyttäjätunnus" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Käyttäjätunnuksen tyyppi" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Luo sähköpostialias ulkoisella ohjauspalvelulla." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ -virhe: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Kohteen sijainti" }, + "fileSend": { + "message": "Tiedosto-Send" + }, "fileSends": { "message": "Tiedosto-Sendit" }, + "textSend": { + "message": "Teksti-Send" + }, "textSends": { "message": "Teksti-Sendit" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Todennetaan" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 80e8361cb34..8a52a1a09c8 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Gumawa ng Account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopyahin ang Password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopyahin ang Note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-fill sa Filipino ay Awtomatikong Pagpuno" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Magtatag ng Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Muling I-generate ang Password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL ng Server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -2464,8 +2502,8 @@ "message": "Maipapayo na mag-require ng password para sa mga user na ma-access ang Send na ito.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Lumikha ng username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Uri ng username" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Bumuo ng isang email alias na may isang panlabas na serbisyo sa pagpapasa." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index aa18348cfda..f8e7dc41687 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Créer un compte" }, + "newToBitwarden": { + "message": "Nouveau sur Bitwarden ?" + }, + "logInWithPasskey": { + "message": "Se connecter avec une clé d'accès" + }, + "useSingleSignOn": { + "message": "Utiliser l'authentification unique" + }, + "welcomeBack": { + "message": "Content de vous revoir" + }, "setAStrongPassword": { "message": "Définir un mot de passe fort" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copier le mot de passe" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copier la note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copier les notes" }, + "fill": { + "message": "Remplir", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Saisie automatique" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Générer un mot de passe" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Régénérer un mot de passe" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Ouvrir le site web" }, + "launchWebsiteName": { + "message": "Lancer le site Web $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Site web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Se connecter" }, + "logInToBitwarden": { + "message": "Se connecter à Bitwarden" + }, "restartRegistration": { "message": "Redémarrer l'inscription" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL du serveur" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL du serveur de l'API" }, @@ -2464,8 +2502,8 @@ "message": "Vous pouvez, si vous le souhaitez, exiger un mot de passe pour accéder à ce Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Ajouter un mot de passe facultatif pour que les destinataires puissent accéder à ce Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Générer un nom d'utilisateur" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Type de nom d'utilisateur" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Générer un alias de courriel avec un service de transfert externe." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erreur $SERVICENAME$ : $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Emplacement de l'élément" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authentification" + }, + "fillGeneratedPassword": { + "message": "Remplir le mot de passe généré", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Mot de passe régénéré", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Enregistrer l'identifiant sur Bitwarden ?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Espace", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Point d'exclamation", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Signe du dollar", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Astérisque", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Tiret bas", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Trait d'union", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Egal à", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Accolade gauche", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Accolade droite", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Crochet gauche", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Crochet droit", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Barre oblique inverse", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Point-virgule", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Guillemets doubles", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Guillemets simples", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Inférieure à", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Supérieur à", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Virgule", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Point", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Point d'interrogation", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Barre oblique", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Minuscule" + }, + "uppercaseAriaLabel": { + "message": "Majuscule" + }, + "generatedPassword": { + "message": "Mot de passe généré" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 139cb2ac4bf..dfaa32981dc 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crea unha conta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar contrasinal" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiar nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-encher" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Xerar contrasinal" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Volver xerar contrasinal" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Sitio web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL do servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do servidor da API" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 8a97a47966f..a991d57b488 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "צור חשבון" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "העתק סיסמה" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "העתק פתק" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "השלמה אוטומטית" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "צור סיסמה" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "צור סיסמה חדשה" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "אתר" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "כתובת שרת" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "כתובת שרת הAPI" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "סוג שם משתמש" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "יצירת כינוי דוא״ל עם שירות העברה חיצוני." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 8d4d1521988..85d99becefc 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create Account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "मजबूत पासवर्ड सेट करें" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy Password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy Note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "स्वत:भरण" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate Password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "वेबसाइट" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "सर्वर URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -2464,8 +2502,8 @@ "message": "वैकल्पिक रूप से उपयोगकर्ताओं को इस सेंड तक पहुंचने के लिए पासवर्ड की आवश्यकता होगी।", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "उपयोगकर्ता नाम बनाएँ" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 4767362352a..d23fed9cda3 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Stvori račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Postavi jaku lozinku" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiraj lozinku" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiraj bilješku" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiraj bilješke" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-ispuna" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generiraj lozinku" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Ponovno generiraj lozinku" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Pokreni web stranicu" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Web stranica" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Prijavi se" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Ponovno pokreni registraciju" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL poslužitelja" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API poslužitelja" }, @@ -2464,8 +2502,8 @@ "message": "Neobavezno zahtijevaj korisnika lozinku za pristup ovom Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Zahtijevaj lozinku za pregled Senda.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generiraj korisničko ime" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip korisničkog imena" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ greška: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Lokacija stavke" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Send datoteke" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Send tekstovi" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Autentifikacija" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index ef6ba5a859e..2582fc75153 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Fiók létrehozása" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Erős jelszó beállítása" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Jelszó másolása" }, + "copyPassphrase": { + "message": "Jelmondat másolása" + }, "copyNote": { "message": "Jegyzet másolása" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Jegyzet másolása" }, + "fill": { + "message": "Kitöltés", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatikus kitöltés" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Jelszó generálása" }, + "generatePassphrase": { + "message": "Jelmondat generálás" + }, "regeneratePassword": { "message": "Jelszó újragenerálása" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Webhely indítása" }, + "launchWebsiteName": { + "message": "$ITEMNAME$ webhely elindítása", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Weboldal" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Szerver URL" }, + "selfHostBaseUrl": { + "message": "Saját üzemeltetésű szerver webcím", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API szerver webcím" }, @@ -2464,8 +2502,8 @@ "message": "Opcionálisan megadhatunk egy jelszót a felhasználók számára a Küldés eléréséhez. ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Jelszó szükséges a Send elem megtekintéséhez.", + "sendPasswordDescV3": { + "message": "Adjunk meg egy opcionális jelszót a címzetteknek a Send eléréséhez.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Felhasználónév generálása" }, + "generateEmail": { + "message": "Email generálása" + }, "usernameType": { "message": "Felhasználónév típusa" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Email álnév generálása külső továbbító szolgáltatással." }, + "forwarderDomainName": { + "message": "Email tartomány", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Válasszunk a kiválasztott szolgáltatás által támogatott tartományt.", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hiba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Elem helyek" }, + "fileSend": { + "message": "Fájl típusú Send" + }, "fileSends": { "message": "Fájl küldés" }, + "textSend": { + "message": "Szöveg típusú Send" + }, "textSends": { "message": "Szöveg küldés" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 8181bdea9c0..2ee5e6855de 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Buat Akun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Salin Kata Sandi" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Salin Catatan" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Isi otomatis" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Buat Kata Sandi" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Buat Ulang Kata Sandi" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Situs Web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL Server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL Server API" }, @@ -2464,8 +2502,8 @@ "message": "Secara opsional, minta kata sandi bagi pengguna untuk mengakses Send ini.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Buat nama pengguna baru" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Jenis nama pengguna" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 3b912c2e039..912fba56266 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Crea account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Imposta una password robusta" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copia password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copia nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Riempimento automatico" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Genera password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Rigenera password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Avvia il sito web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Sito web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Accedi" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Riprova la registrazione" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL del server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL del server API" }, @@ -2464,8 +2502,8 @@ "message": "Richiedi una password agli utenti per accedere a questo Send (facoltativo).", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Genera nome utente" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo di nome utente" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genera un alias email con un servizio di inoltro esterno." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Errore $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Posizione elemento" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Send File" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Send Testo" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index b7da0c89584..59fe8fcf708 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "アカウントの作成" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "強力なパスワードを設定する" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "パスワードをコピー" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "メモをコピー" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "メモをコピー" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "自動入力" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "パスワードの自動生成" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "パスワードの再生成" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "ウェブサイトを開く" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ウェブサイト" }, @@ -814,6 +845,9 @@ "logIn": { "message": "ログイン" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "登録を再度始める" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "サーバー URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API サーバー URL" }, @@ -2464,8 +2502,8 @@ "message": "必要に応じて、ユーザーがこの Send にアクセスするためのパスワードを要求します。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Send を表示するにはこのパスワードが必要になります。", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "ユーザー名を生成" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "ユーザー名の種類" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "外部転送サービスを使用してメールエイリアスを生成します。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ エラー: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "アイテムの場所" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "ファイル Send" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "テキスト Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "認証中" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 3c487c9dfa1..1d38c4b53ea 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ანგარიშის შექმნა" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "პაროლის კოპირება" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ავტომატური შევსება" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "პაროლის გენერირება" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ვებგვერდი" }, @@ -814,6 +845,9 @@ "logIn": { "message": "შესვლა" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "სერვერის URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "ავთენტიკაცია" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index acf6340df91..c58b6e5ff80 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ಖಾತೆ ತೆರೆ" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "ಪಾಸ್ವರ್ಡ್ ನಕಲಿಸಿ" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "ಟಿಪ್ಪಣಿ ನಕಲಿಸಿ" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ಸ್ವಯಂ ಭರ್ತಿ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಪುನರುತ್ಪಾದಿಸಿ" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "ಜಾಲತಾಣ" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "ಸರ್ವರ್ URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API ಸರ್ವರ್ URL" }, @@ -2464,8 +2502,8 @@ "message": "ಈ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಲು ಬಳಕೆದಾರರಿಗೆ ಪಾಸ್‌ವರ್ಡ್ ಐಚ್ ಗತ್ಯವಿದೆ.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 7a03c87713a..e9b012cc277 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "계정 만들기" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "비밀번호 설정" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "비밀번호 복사" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "메모 복사" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "자동 완성" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "비밀번호 생성" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "비밀번호 재생성" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "웹사이트 열기" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "웹 사이트" }, @@ -814,6 +845,9 @@ "logIn": { "message": "로그인" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "서버 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 서버 URL" }, @@ -2464,8 +2502,8 @@ "message": "이 Send에 접근하기 위해 암호를 입력하도록 선택적으로 요구합니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "아이디 생성" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "아이디 유형" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index fe3d17678ee..660cf62b697 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Sukurti paskyrą" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Nustatyti stiprų slaptažodį" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopijuoti slaptažodį" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopijuoti pastabą" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatinis užpildymas" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Sugeneruoti slaptažodį" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Generuoti slaptažodį iš naujo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Atidaryti svetainę" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Tinklapis" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Serverio URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API serverio nuoroda" }, @@ -2464,8 +2502,8 @@ "message": "Pasirinktinai reikalauti slaptažodžio, kad vartotojai galėtų pasiekti šį „Send“.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generuoti vartotojo vardą" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Vartotojo prisijungimo vardo tipas" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Sugeneruoti el. pašto slapyvardį su išorine persiuntimo paslauga." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "„$SERVICENAME$“ klaida: $ERRORMESSAGE$.", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 72622132b4b..56321c97cba 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Izveidot kontu" }, + "newToBitwarden": { + "message": "Bitwarden iepriekš nav izmantots?" + }, + "logInWithPasskey": { + "message": "Pieteikties ar piekļuves atslēgu" + }, + "useSingleSignOn": { + "message": "Izmantot vienoto pieteikšanos" + }, + "welcomeBack": { + "message": "Laipni lūdzam atpakaļ" + }, "setAStrongPassword": { "message": "Jāiestata droša parole" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Ievietot paroli starpliktuvē" }, + "copyPassphrase": { + "message": "Ievietot paroles vārdkopu starpliktuvē" + }, "copyNote": { "message": "Ievietot piezīmi starpliktuvē" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Ievietot piezīmes starpliktuvē" }, + "fill": { + "message": "Aizpildīt", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automātiskā aizpilde" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Veidot paroli" }, + "generatePassphrase": { + "message": "Izveidot paroles vārdkopu" + }, "regeneratePassword": { "message": "Pārizveidot paroli" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Atvērt tīmekļvietni" }, + "launchWebsiteName": { + "message": "Palaist tīmekļvietni $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Tīmekļa vietne" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Pieteikties" }, + "logInToBitwarden": { + "message": "Pieteikties Bitwarden" + }, "restartRegistration": { "message": "Sākt reģistrēšanos no jauna" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Servera URL" }, + "selfHostBaseUrl": { + "message": "Pašmitināta servera URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API servera URL" }, @@ -2464,8 +2502,8 @@ "message": "Pēc izvēles pieprasīt paroli, lai lietotāji varētu piekļūt šim Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Pieprasīt šo paroli, lai apskatītu Send.", + "sendPasswordDescV3": { + "message": "Pēc izvēles pievieno paroli, lai saņēmēji varētu piekļūt šim Send!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Izveidot lietotājvārdu" }, + "generateEmail": { + "message": "Izveidot e-pastu" + }, "usernameType": { "message": "Lietotājvārda veids" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Izveidot e-pastu aizstājvārdu ar ārēju pārvirzīšanas pakalpojumu." }, + "forwarderDomainName": { + "message": "E-pasta domēns", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Jāizvēlas domēns, kuru atbalsta atlasītais pakalpojums", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ kļūda: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Vienuma atrašanās vieta" }, + "fileSend": { + "message": "Datnes Send" + }, "fileSends": { "message": "Datņu Send" }, + "textSend": { + "message": "Teksta Send" + }, "textSends": { "message": "Teksta Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Autentificē" + }, + "fillGeneratedPassword": { + "message": "Aizpildīt izveidoto paroli", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Parole pārizveidota", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Saglabāt pieteikšanās vienumu Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Atstarpe", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Atpakaļpēdiņa", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Izsaukuma zīme", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At zīme", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Restes zīme", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolāra zīme", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Procentu zīme", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Jumtiņš", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Un zīme", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisks", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Kreisās iekavas", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Labās iekavas", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Apakšsvītra", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Defise", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Pluss", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Vienādības zīme", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Kreisā figūriekava", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Labā figūriekava", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Kreisā kvadrātiekava", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Labā kvadrātiekava", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Stateniska svītra", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Atpakaļslīpsvītra", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Kols", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semikols", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Divkāršās pēdiņas", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Vienpēdiņas", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Mazāks par", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Lielāks par", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Komats", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Punkts", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Jautājuma zīme", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Slīpsvītra", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Mazie burti" + }, + "uppercaseAriaLabel": { + "message": "Lielie burti" + }, + "generatedPassword": { + "message": "Izveidotā parole" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 603324f548f..45576592038 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "അക്കൗണ്ട് സൃഷ്ടിക്കുക" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "പാസ്‌വേഡ് പകർത്തുക" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "കുറിപ്പ് പകർത്തുക" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ഓട്ടോഫിൽ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "പാസ്‌വേഡ് സൃഷ്ടിക്കുക" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "പാസ്സ്‌വേഡ് വീണ്ടും സൃഷ്ടിക്കുക" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "വെബ്സൈറ്റ്" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "സെർവർ URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API സെർവർ URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index ae0112d1dd0..083e61c3b94 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "खाते तयार करा" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "पासवर्ड कॉपी करा" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "टीप कॉपी करा" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "स्वयंभरण" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "पासवर्ड पुनर्जनित करा" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "संकेतस्थळ" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 3f634e272a6..19c1fd0e89e 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Opprett en konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopier passordet" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopier notatet" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-utfylling" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generer et passord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Omgenerer et passord" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Nettsted" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Tjener-nettadresse" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-tjenernettadresse" }, @@ -2464,8 +2502,8 @@ "message": "Eventuelt krever et passord for brukere å få tilgang til denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generer brukernavn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Brukernavntype" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generer et e-postalias med en ekstern videresendingstjeneste." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 878518531f8..c36a0018b90 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Account aanmaken" }, + "newToBitwarden": { + "message": "Nieuw bij Bitwarden?" + }, + "logInWithPasskey": { + "message": "Inloggen met passkey" + }, + "useSingleSignOn": { + "message": "Single sign-on gebruiken" + }, + "welcomeBack": { + "message": "Welkom terug" + }, "setAStrongPassword": { "message": "Sterk wachtwoord instellen" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Wachtwoord kopiëren" }, + "copyPassphrase": { + "message": "Wachtwoordzin kopiëren" + }, "copyNote": { "message": "Notitie kopiëren" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notities kopiëren" }, + "fill": { + "message": "Invullen", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Auto-invullen" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Wachtwoord genereren" }, + "generatePassphrase": { + "message": "Wachtwoordzin genereren" + }, "regeneratePassword": { "message": "Wachtwoord opnieuw genereren" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Website openen" }, + "launchWebsiteName": { + "message": "Start website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Inloggen" }, + "logInToBitwarden": { + "message": "Inloggen op Bitwarden" + }, "restartRegistration": { "message": "Registratie herstarten" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "URL zelfgehoste server", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server-URL" }, @@ -2464,8 +2502,8 @@ "message": "Vereis optioneel een wachtwoord voor gebruikers om toegang te krijgen tot deze Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Dit wachtwoord vereisen voor het weergeven van de Send.", + "sendPasswordDescV3": { + "message": "Voeg een optioneel wachtwoord toe voor ontvangers om toegang te krijgen tot deze Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Gebruikersnamen genereren" }, + "generateEmail": { + "message": "E-mailadres genereren" + }, "usernameType": { "message": "Type gebruikersnaam" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Genereer een e-mailalias met een externe doorschakelservice." }, + "forwarderDomainName": { + "message": "E-maildomein", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Kies een domein dat wordt ondersteund door de geselecteerde dienst", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Itemlocatie" }, + "fileSend": { + "message": "Bestand-Sends" + }, "fileSends": { "message": "Bestand-Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Tekst-Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Aan het inloggen" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 7340d09f8df..b71035cfaff 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Utwórz konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Ustaw silne hasło" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiuj hasło" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiuj notatkę" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiuj notatki" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autouzupełnianie" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Wygeneruj hasło" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Wygeneruj ponownie hasło" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Otwórz stronę" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Strona" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Zaloguj się" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Zrestartuj rejestrację" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Adres URL serwera" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Adres URL serwera API" }, @@ -2464,8 +2502,8 @@ "message": "Opcjonalne hasło dla użytkownika, aby uzyskać dostęp do wysyłki.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Wymagaj tego hasła aby wyświetlić Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Wygeneruj nazwę użytkownika" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Rodzaj nazwy użytkownika" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekierowania." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Błąd $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Lokalizacja elementu" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 11a277c41b6..a5509efc487 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Criar Conta" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Defina uma senha forte" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar Senha" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiar Nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copiar Notas" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autopreencher" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Gerar Senha" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Gerar Nova Senha" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Abrir site" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Site" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Fazer login" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Reiniciar registro" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL do Servidor" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do Servidor da API" }, @@ -2464,8 +2502,8 @@ "message": "Exigir opcionalmente 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." }, - "sendPasswordDescV2": { - "message": "Exigir essa senha para visualizar o Envio.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Gerar Usuário" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tipo de usuário" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Gere um apelido de e-mail com um serviço de encaminhamento externo." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Localização do Item" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Arquivos enviados" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Texto enviado" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Autenticando" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 2bdb2b5a935..244f700dac3 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Criar conta" }, + "newToBitwarden": { + "message": "Novo no Bitwarden?" + }, + "logInWithPasskey": { + "message": "Iniciar sessão com a chave de acesso" + }, + "useSingleSignOn": { + "message": "Utilizar início de sessão único" + }, + "welcomeBack": { + "message": "Bem-vindo de volta" + }, "setAStrongPassword": { "message": "Defina uma palavra-passe forte" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiar palavra-passe" }, + "copyPassphrase": { + "message": "Copiar frase de acesso" + }, "copyNote": { "message": "Copiar nota" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copiar notas" }, + "fill": { + "message": "Preencher", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Preencher automaticamente" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Gerar palavra-passe" }, + "generatePassphrase": { + "message": "Gerar frase de acesso" + }, "regeneratePassword": { "message": "Regenerar palavra-passe" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Iniciar site" }, + "launchWebsiteName": { + "message": "Iniciar site $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Site" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Iniciar sessão" }, + "logInToBitwarden": { + "message": "Iniciar sessão no Bitwarden" + }, "restartRegistration": { "message": "Reiniciar registo" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL do servidor" }, + "selfHostBaseUrl": { + "message": "URL do servidor auto-hospedado", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL do servidor da API" }, @@ -2464,8 +2502,8 @@ "message": "Opcionalmente, exigir uma palavra-passe para os utilizadores acederem a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Esta palavra-passe é necessária para visualizar o Send.", + "sendPasswordDescV3": { + "message": "Adicione uma palavra-passe opcional para os destinatários acederem a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Gerar nome de utilizador" }, + "generateEmail": { + "message": "Gerar e-mail" + }, "usernameType": { "message": "Tipo de nome de utilizador" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Gerar um alias de e-mail com um serviço de reencaminhamento externo." }, + "forwarderDomainName": { + "message": "Domínio de e-mail", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Escolha um domínio que seja suportado pelo serviço selecionado", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Erro no $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Localização do item" }, + "fileSend": { + "message": "Send de ficheiro" + }, "fileSends": { "message": "Sends de ficheiros" }, + "textSend": { + "message": "Send de texto" + }, "textSends": { "message": "Sends de texto" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "A autenticar" + }, + "fillGeneratedPassword": { + "message": "Preencher a palavra-passe gerada", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Palavra-passe gerada novamente", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Guardar credencial no Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Espaço", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Til", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Ponto de exclamação", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisco", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Parêntesis esquerdo", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Parêntesis direito", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hífen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Mais", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Igual", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Dois pontos", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Ponto e vírgula", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Menor", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Maior", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Vírgula", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Ponto final", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Ponto de interrogação", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Minúsculas" + }, + "uppercaseAriaLabel": { + "message": "Maiúsculas" + }, + "generatedPassword": { + "message": "Palavra-passe gerada" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 7b506d5a3e9..4847fecf059 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Creare cont" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Setați o parolă puternică" }, @@ -72,7 +84,7 @@ "message": "Alăturați-vă organizației" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Alătură-te $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copiere parolă" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copiere notă" }, @@ -138,7 +153,7 @@ "message": "Copiați numărul de licență" }, "copyCustomField": { - "message": "Copy $FIELD$", + "message": "Copiază $FIELD$", "placeholders": { "field": { "content": "$1", @@ -147,10 +162,14 @@ } }, "copyWebsite": { - "message": "Copy website" + "message": "Copiază site-ul" }, "copyNotes": { - "message": "Copy notes" + "message": "Copiază notițele" + }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { "message": "Auto-completare" @@ -204,16 +223,16 @@ "message": "Adăugare articol" }, "accountEmail": { - "message": "Account email" + "message": "Adresa de email a contului" }, "requestHint": { - "message": "Request hint" + "message": "Solicită indiciu" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "Solicită indiciu parolă" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "Enter your account email address and your password hint will be sent to you" + "message": "Introduceți adresa de e-mail a contului și indiciul pentru parolă va fi trimis pe email" }, "passwordHint": { "message": "Indiciu parolă" @@ -338,10 +357,10 @@ "message": "Editare dosar" }, "newFolder": { - "message": "New folder" + "message": "Folder nou" }, "folderName": { - "message": "Folder name" + "message": "Numele folderului" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generare parolă" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerare parolă" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Lansați siteul web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Sait web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Autentificare" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Reporniți înregistrarea" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL server" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL server API" }, @@ -2464,8 +2502,8 @@ "message": "Opțional, este necesară o parolă pentru ca utilizatorii să acceseze acest Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generare nume de utilizator" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Tip de nume de utilizator" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generați un alias de e-mail cu un serviciu de redirecționare extern." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 56a58cfb9f1..9df9fc8d567 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Создать аккаунт" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Задайте надежный пароль" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Скопировать пароль" }, + "copyPassphrase": { + "message": "Скопировать парольную фразу" + }, "copyNote": { "message": "Скопировать заметку" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Скопировать заметки" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Автозаполнение" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Сгенерировать пароль" }, + "generatePassphrase": { + "message": "Создать парольную фразу" + }, "regeneratePassword": { "message": "Создать новый пароль" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Открыть сайт" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Сайт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Войти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Перезапустить регистрацию" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL сервера" }, + "selfHostBaseUrl": { + "message": "URL собственного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API сервера" }, @@ -2464,8 +2502,8 @@ "message": "По возможности запрашивать у пользователей пароль для доступа к этой Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Требовать этот пароль для просмотра Send.", + "sendPasswordDescV3": { + "message": "Добавьте опциональный пароль для доступа получателей к этой Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Создать имя пользователя" }, + "generateEmail": { + "message": "Сгенерировать email" + }, "usernameType": { "message": "Тип имени пользователя" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Создать псевдоним электронной почты для внешней службы пересылки." }, + "forwarderDomainName": { + "message": "Домен электронной почты", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Выберите домен, который поддерживается выбранным сервисом", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Ошибка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Расположение элемента" }, + "fileSend": { + "message": "Файловая Send" + }, "fileSends": { "message": "Файловая Send" }, + "textSend": { + "message": "Текстовая Send" + }, "textSends": { "message": "Текстовая Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Аутентификация" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 1636f483ed6..a4b8c3b8149 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "ගිණුමක් සාදන්න" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "මුරපදය පිටපත් කරන්න" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "සටහන පිටපත් කරන්න" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "ස්වයං-පිරවීම" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "මුරපදය ජනනය කරන්න" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "මුරපදය ප්රතිජනනය" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "වියමන අඩවිය" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "සේවාදායකය URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API සේවාදායකය URL" }, @@ -2464,8 +2502,8 @@ "message": "විකල්පයක් ලෙස පරිශීලකයින්ට මෙම යවන්න වෙත ප්රවේශ වීමට මුරපදයක් අවශ්ය වේ.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 1bf25cac9f8..f08b18f33ba 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Vytvoriť účet" }, + "newToBitwarden": { + "message": "Ste noví na Bitwardene?" + }, + "logInWithPasskey": { + "message": "Prihlásiť sa s prístupovým kľúčom" + }, + "useSingleSignOn": { + "message": "Použiť jednotné prihlásenie" + }, + "welcomeBack": { + "message": "Vitajte späť" + }, "setAStrongPassword": { "message": "Nastavte silné heslo" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopírovať heslo" }, + "copyPassphrase": { + "message": "Kopírovať prístupovú frázu" + }, "copyNote": { "message": "Kopírovať poznámku" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopírovať poznámky" }, + "fill": { + "message": "Vyplniť", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Automatické vypĺňanie" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generovať heslo" }, + "generatePassphrase": { + "message": "Generovať prístupovú frázu" + }, "regeneratePassword": { "message": "Vygenerovať nové heslo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Otvoriť stránku" }, + "launchWebsiteName": { + "message": "Otvoriť stránku $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webstránka" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Prihlásiť sa" }, + "logInToBitwarden": { + "message": "Prihlásiť sa do Bitwardenu" + }, "restartRegistration": { "message": "Zopakovať registráciu" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL servera" }, + "selfHostBaseUrl": { + "message": "Adresa URL vlastného hostingu", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL API servera" }, @@ -2464,8 +2502,8 @@ "message": "Voliteľne môžete vyžadovať heslo pre používateľov na prístup k tomuto Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Na zobrazenie tohto Sendu vyžadovať toto heslo.", + "sendPasswordDescV3": { + "message": "Pridajte voliteľné heslo pre príjemcov na prístup k tomuto Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Vygenerovať používateľské meno" }, + "generateEmail": { + "message": "Generovať e-mail" + }, "usernameType": { "message": "Typ používateľského mena" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Vytvoriť e-mailový alias pomocou externej služby preposielania." }, + "forwarderDomainName": { + "message": "E-mailová doména", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Vyberte doménu, ktorá je podporovaná vybranou službou", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ chyba: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Umiestnenie položky" }, + "fileSend": { + "message": "Send so súborom" + }, "fileSends": { "message": "Sendy so súborom" }, + "textSend": { + "message": "Textový Send" + }, "textSends": { "message": "Textové Sendy" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Overuje sa" + }, + "fillGeneratedPassword": { + "message": "Vložiť vygenerované heslo", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Vygenerované nové heslo", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Uložiť prihlasovacie údaje do Bitwardenu?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Medzera", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilda", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Opačný dĺžeň", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Výkričník", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "Zavináč", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Mriežka", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolár", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percento", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Striežka", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Hviezdička", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Ľavá zátvorka", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Pravá zátvorka", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Podčiarkovník", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Spojovník", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Rovná sa", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Ľavá zložená zátvorka", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Pravá zložená zátvorka", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Ľavá hranatá zátvorka", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Pravá hranatá zátvorka", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Rúra", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Spätná lomka", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Dvojbodka", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Bodkočiarka", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Dvojité úvodzovky", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Jednoduché úvodzovky", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Ľavá lomená zátvorka", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Pravá lomená zátvorka", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Čiarka", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Bodka", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Otáznik", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Lomka", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Malé písmená" + }, + "uppercaseAriaLabel": { + "message": "Veľké písmená" + }, + "generatedPassword": { + "message": "Vygenerované heslo" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 9fab3ca10df..ea2d29dfe3c 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Ustvari račun" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiraj geslo" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiraj opombo" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Samodejno izpolnjevanje" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generiraj geslo" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Ponovno ustvari geslo" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Spletna stran" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL naslov strežnika" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL naslov API strežnika" }, @@ -2464,8 +2502,8 @@ "message": "Za dostop do te pošiljke lahko nastavite geslo.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Ustvari uporabniško ime" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Vrsta uporabniškega imena" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 484a44af4d1..ea1a030e07b 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Креирај налог" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Поставите јаку лозинку" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Копирај лозинку" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Копирај белешку" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Копирати белешке" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Аутоматско допуњавање" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Генерисање лозинке" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Поново генериши лозинку" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Покрените веб локацију" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Веб сајт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Пријави се" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Поново покрените регистрацију" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "УРЛ Сервера" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "УРЛ АПИ Сервера" }, @@ -2464,8 +2502,8 @@ "message": "Опционално захтевајте лозинку за приступ корисницима „Send“-у.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Захтева ову лозинку за преглед Send-а.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генериши име" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Тип имена" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ грешка: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Смештај ставке" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "Датотека „Send“" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Текст „Send“" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Аутентификација" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index bbc33e9456e..175f4824179 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Skapa konto" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Ställ in ett starkt lösenord" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Kopiera lösenord" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Kopiera anteckning" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Kopiera anteckningar" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Fyll i automatiskt" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generera lösenord" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Återskapa lösenord" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Webbplats" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Logga in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server-URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API-server-URL" }, @@ -2464,8 +2502,8 @@ "message": "Kräv valfritt ett lösenord för att användare ska komma åt denna Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generera användarnamn" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Typ av användarnamn" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Skapa ett e-postalias med en extern vidarebefordranstjänst." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$-fel: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 290663f4347..4037887cb28 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Create account" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Copy password" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Autofill" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Website" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Server URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 66903ea6e6e..e157ac74fed 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "สร้างบัญชี" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Set a strong password" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "คัดลอกรหัสผ่าน" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Copy Note" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "กรอกข้อมูลอัตโนมัติ" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Generate Password" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Regenerate Password" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Launch website" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "เว็บไซต์" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Log in" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL ของเซิร์ฟเวอร์" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API Server URL" }, @@ -2464,8 +2502,8 @@ "message": "Optionally require a password for users to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Generate username" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Username type" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index a7ea5dfd146..7ed5f94e29d 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Hesap oluştur" }, + "newToBitwarden": { + "message": "Bitwarden'da yeni misiniz?" + }, + "logInWithPasskey": { + "message": "Geçiş anahtarıyla giriş yap" + }, + "useSingleSignOn": { + "message": "Çoklu oturum açma kullan" + }, + "welcomeBack": { + "message": "Tekrar hoş geldiniz" + }, "setAStrongPassword": { "message": "Güçlü bir parola belirleyin" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Parolayı kopyala" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Notu kopyala" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Notları kopyala" }, + "fill": { + "message": "Doldur", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Otomatik doldur" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Parola oluştur" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Yeni parola oluştur" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Web sitesini aç" }, + "launchWebsiteName": { + "message": "$ITEMNAME$ web sitesini aç", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Web sitesi" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Giriş yap" }, + "logInToBitwarden": { + "message": "Bitwarden'a giriş yapın" + }, "restartRegistration": { "message": "Kaydı yeniden başlat" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "Sunucu URL'si" }, + "selfHostBaseUrl": { + "message": "Kendi kendine barındırılan sunucu URL'si", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API sunucu URL'si" }, @@ -2464,8 +2502,8 @@ "message": "Kullanıcıların bu Send'e erişmek için parola girmelerini isteyebilirsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Send'i görüntülemek için bu parolayı iste.", + "sendPasswordDescV3": { + "message": "Alıcıların bu Send'e erişmesi için isterseniz parola ekleyebilirsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Kullanıcı adı oluştur" }, + "generateEmail": { + "message": "E-posta oluştur" + }, "usernameType": { "message": "Kullanıcı adı türü" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Harici bir yönlendirme servisiyle e-posta maskesi oluştur." }, + "forwarderDomainName": { + "message": "E-posta alan adı", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Seçtiğiniz servisin desteklediği bir alan adı seçin", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ hatası: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Kayıt konumu" }, + "fileSend": { + "message": "Dosya Send'i" + }, "fileSends": { "message": "Dosya Send'leri" }, + "textSend": { + "message": "Metin Send'i" + }, "textSends": { "message": "Metin Send'leri" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Kimlik doğrulanıyor" + }, + "fillGeneratedPassword": { + "message": "Üretilen parolayı doldur", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Parola yeniden üretildi", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Hesap Bitwarden'a kaydedilsin mi?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Boşluk", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Ters tırnak", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Ünlem işareti", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At işareti", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Kare işareti", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dolar işareti", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Yüzde işareti", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Düzeltme işareti", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ve işareti", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Yıldız işareti", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Sol parantez", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Sağ parantez", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Alt çizgi", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Tire", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Artı", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Eşittir", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Sol küme parantezi", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Sağ küme parantezi", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Sol köşeli parantez", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Sağ köşeli parantez", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Çubuk", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Ters eğik çizgi", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "İki nokta", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Noktalı virgül", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Çift tırnak", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Tek tırnak", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Küçüktür", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Büyüktür", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Virgül", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Nokta", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Soru işareti", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Bölü işareti", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Küçük harf" + }, + "uppercaseAriaLabel": { + "message": "Büyük harf" + }, + "generatedPassword": { + "message": "Üretilen parola" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index d5b6d5ade70..74544eca2f2 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Створити обліковий запис" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Встановіть надійний пароль" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Копіювати пароль" }, + "copyPassphrase": { + "message": "Копіювати парольну фразу" + }, "copyNote": { "message": "Копіювати нотатку" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Копіювати нотатки" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Автозаповнення" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Генерувати пароль" }, + "generatePassphrase": { + "message": "Генерувати парольну фразу" + }, "regeneratePassword": { "message": "Генерувати новий" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Відкрити вебсайт" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Вебсайт" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Увійти" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Перезапустити реєстрацію" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL-адреса сервера" }, + "selfHostBaseUrl": { + "message": "URL-адреса власного сервера", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "URL-адреса сервера API" }, @@ -2464,8 +2502,8 @@ "message": "Ви можете встановити пароль для доступу до цього відправлення.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Вимагати цей пароль для перегляду відправлення.", + "sendPasswordDescV3": { + "message": "За бажання додайте пароль для отримувачів цього відправлення.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Генерувати ім'я користувача" }, + "generateEmail": { + "message": "Генерувати е-пошту" + }, "usernameType": { "message": "Тип імені користувача" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Згенеруйте псевдонім е-пошти зі стороннім сервісом пересилання." }, + "forwarderDomainName": { + "message": "Домен електронної пошти", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Виберіть домен, який підтримується вибраною службою", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Помилка $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Розташування запису" }, + "fileSend": { + "message": "Відправлення файлу" + }, "fileSends": { "message": "Відправлення файлів" }, + "textSend": { + "message": "Відправлення тексту" + }, "textSends": { "message": "Відправлення тексту" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Аутентифікація" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index a378a56aa9f..37dfd796c6b 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "Tạo tài khoản" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "Đặt mật khẩu mạnh" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "Sao chép mật khẩu" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "Sao chép ghi chú" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "Copy notes" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "Tự động điền" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "Tạo mật khẩu" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "Tạo lại mật khẩu" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "Mở trang web" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "Trang web" }, @@ -814,6 +845,9 @@ "logIn": { "message": "Đăng nhập" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Tiến hành đăng ký lại" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "URL máy chủ" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "Địa chỉ API máy chủ" }, @@ -2464,8 +2502,8 @@ "message": "Yêu cầu nhập mật khẩu khi người dùng truy cập vào phần Gửi này.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "Tạo tên người dùng" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "Loại tên người dùng" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "Tạo bí danh email với dịch vụ chuyển tiếp bên ngoài." }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "Lỗi $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Vị trí mục" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index ad89f8efc79..20711539af5 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "创建账户" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "设置强密码" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "复制密码" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "复制备注" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "复制备注" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "自动填充" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "生成密码" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "重新生成密码" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "启动网站" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "网站" }, @@ -814,6 +845,9 @@ "logIn": { "message": "登录" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "重新开始注册" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "服务器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 服务器 URL" }, @@ -2464,8 +2502,8 @@ "message": "可选,用户需要提供密码才能访问此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "需要此密码才能查看此 Send。", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "生成用户名" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "用户名类型" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "使用外部转发服务生成一个电子邮件别名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ 错误:$ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "项目位置" }, + "fileSend": { + "message": "文件 Send" + }, "fileSends": { "message": "文件 Send" }, + "textSend": { + "message": "文本 Send" + }, "textSends": { "message": "文本 Send" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "正在验证" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 496d7b8159a..a6a8d933b82 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -19,6 +19,18 @@ "createAccount": { "message": "建立帳戶" }, + "newToBitwarden": { + "message": "New to Bitwarden?" + }, + "logInWithPasskey": { + "message": "Log in with passkey" + }, + "useSingleSignOn": { + "message": "Use single sign-on" + }, + "welcomeBack": { + "message": "Welcome back" + }, "setAStrongPassword": { "message": "設定一個強密碼" }, @@ -107,6 +119,9 @@ "copyPassword": { "message": "複製密碼" }, + "copyPassphrase": { + "message": "Copy passphrase" + }, "copyNote": { "message": "複製備註" }, @@ -152,6 +167,10 @@ "copyNotes": { "message": "複製備註" }, + "fill": { + "message": "Fill", + "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." + }, "autoFill": { "message": "自動填入" }, @@ -407,6 +426,9 @@ "generatePassword": { "message": "產生密碼" }, + "generatePassphrase": { + "message": "Generate passphrase" + }, "regeneratePassword": { "message": "重新產生密碼" }, @@ -568,6 +590,15 @@ "launchWebsite": { "message": "開啟網站" }, + "launchWebsiteName": { + "message": "Launch website $ITEMNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret item" + } + } + }, "website": { "message": "網站" }, @@ -814,6 +845,9 @@ "logIn": { "message": "登入" }, + "logInToBitwarden": { + "message": "Log in to Bitwarden" + }, "restartRegistration": { "message": "Restart registration" }, @@ -1389,6 +1423,10 @@ "baseUrl": { "message": "伺服器 URL" }, + "selfHostBaseUrl": { + "message": "Self-host server URL", + "description": "Label for field requesting a self-hosted integration service URL" + }, "apiUrl": { "message": "API 伺服器網址" }, @@ -2464,8 +2502,8 @@ "message": "選用功能。使用者需提供密碼才能存取此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendPasswordDescV2": { - "message": "Require this password to view the Send.", + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2827,6 +2865,9 @@ "generateUsername": { "message": "產生使用者名稱" }, + "generateEmail": { + "message": "Generate email" + }, "usernameType": { "message": "使用者名稱類型" }, @@ -2867,6 +2908,14 @@ "forwardedEmailDesc": { "message": "使用外部轉寄服務產生一個電子郵件別名。" }, + "forwarderDomainName": { + "message": "Email domain", + "description": "Labels the domain name email forwarder service option" + }, + "forwarderDomainNameHint": { + "message": "Choose a domain that is supported by the selected service", + "description": "Guidance provided for email forwarding services that support multiple email domains." + }, "forwarderError": { "message": "$SERVICENAME$ error: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", @@ -4494,9 +4543,15 @@ "itemLocation": { "message": "Item Location" }, + "fileSend": { + "message": "File Send" + }, "fileSends": { "message": "File Sends" }, + "textSend": { + "message": "Text Send" + }, "textSends": { "message": "Text Sends" }, @@ -4559,5 +4614,158 @@ }, "authenticating": { "message": "Authenticating" + }, + "fillGeneratedPassword": { + "message": "Fill generated password", + "description": "Heading for the password generator within the inline menu" + }, + "passwordRegenerated": { + "message": "Password regenerated", + "description": "Notification message for when a password has been regenerated" + }, + "saveLoginToBitwarden": { + "message": "Save login to Bitwarden?", + "description": "Confirmation message for saving a login to Bitwarden" + }, + "spaceCharacterDescriptor": { + "message": "Space", + "description": "Represents the space key in screen reader content as a readable word" + }, + "tildeCharacterDescriptor": { + "message": "Tilde", + "description": "Represents the ~ key in screen reader content as a readable word" + }, + "backtickCharacterDescriptor": { + "message": "Backtick", + "description": "Represents the ` key in screen reader content as a readable word" + }, + "exclamationCharacterDescriptor": { + "message": "Exclamation mark", + "description": "Represents the ! key in screen reader content as a readable word" + }, + "atSignCharacterDescriptor": { + "message": "At sign", + "description": "Represents the @ key in screen reader content as a readable word" + }, + "hashSignCharacterDescriptor": { + "message": "Hash sign", + "description": "Represents the # key in screen reader content as a readable word" + }, + "dollarSignCharacterDescriptor": { + "message": "Dollar sign", + "description": "Represents the $ key in screen reader content as a readable word" + }, + "percentSignCharacterDescriptor": { + "message": "Percent sign", + "description": "Represents the % key in screen reader content as a readable word" + }, + "caretCharacterDescriptor": { + "message": "Caret", + "description": "Represents the ^ key in screen reader content as a readable word" + }, + "ampersandCharacterDescriptor": { + "message": "Ampersand", + "description": "Represents the & key in screen reader content as a readable word" + }, + "asteriskCharacterDescriptor": { + "message": "Asterisk", + "description": "Represents the * key in screen reader content as a readable word" + }, + "parenLeftCharacterDescriptor": { + "message": "Left parenthesis", + "description": "Represents the ( key in screen reader content as a readable word" + }, + "parenRightCharacterDescriptor": { + "message": "Right parenthesis", + "description": "Represents the ) key in screen reader content as a readable word" + }, + "hyphenCharacterDescriptor": { + "message": "Underscore", + "description": "Represents the _ key in screen reader content as a readable word" + }, + "underscoreCharacterDescriptor": { + "message": "Hyphen", + "description": "Represents the - key in screen reader content as a readable word" + }, + "plusCharacterDescriptor": { + "message": "Plus", + "description": "Represents the + key in screen reader content as a readable word" + }, + "equalsCharacterDescriptor": { + "message": "Equals", + "description": "Represents the = key in screen reader content as a readable word" + }, + "braceLeftCharacterDescriptor": { + "message": "Left brace", + "description": "Represents the { key in screen reader content as a readable word" + }, + "braceRightCharacterDescriptor": { + "message": "Right brace", + "description": "Represents the } key in screen reader content as a readable word" + }, + "bracketLeftCharacterDescriptor": { + "message": "Left bracket", + "description": "Represents the [ key in screen reader content as a readable word" + }, + "bracketRightCharacterDescriptor": { + "message": "Right bracket", + "description": "Represents the ] key in screen reader content as a readable word" + }, + "pipeCharacterDescriptor": { + "message": "Pipe", + "description": "Represents the | key in screen reader content as a readable word" + }, + "backSlashCharacterDescriptor": { + "message": "Back slash", + "description": "Represents the back slash key in screen reader content as a readable word" + }, + "colonCharacterDescriptor": { + "message": "Colon", + "description": "Represents the : key in screen reader content as a readable word" + }, + "semicolonCharacterDescriptor": { + "message": "Semicolon", + "description": "Represents the ; key in screen reader content as a readable word" + }, + "doubleQuoteCharacterDescriptor": { + "message": "Double quote", + "description": "Represents the double quote key in screen reader content as a readable word" + }, + "singleQuoteCharacterDescriptor": { + "message": "Single quote", + "description": "Represents the ' key in screen reader content as a readable word" + }, + "lessThanCharacterDescriptor": { + "message": "Less than", + "description": "Represents the < key in screen reader content as a readable word" + }, + "greaterThanCharacterDescriptor": { + "message": "Greater than", + "description": "Represents the > key in screen reader content as a readable word" + }, + "commaCharacterDescriptor": { + "message": "Comma", + "description": "Represents the , key in screen reader content as a readable word" + }, + "periodCharacterDescriptor": { + "message": "Period", + "description": "Represents the . key in screen reader content as a readable word" + }, + "questionCharacterDescriptor": { + "message": "Question mark", + "description": "Represents the ? key in screen reader content as a readable word" + }, + "forwardSlashCharacterDescriptor": { + "message": "Forward slash", + "description": "Represents the / key in screen reader content as a readable word" + }, + "lowercaseAriaLabel": { + "message": "Lowercase" + }, + "uppercaseAriaLabel": { + "message": "Uppercase" + }, + "generatedPassword": { + "message": "Generated password" } } diff --git a/apps/browser/store/locales/be/copy.resx b/apps/browser/store/locales/be/copy.resx index 65c337826bc..b11a1e325c6 100644 --- a/apps/browser/store/locales/be/copy.resx +++ b/apps/browser/store/locales/be/copy.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden Password Manager + Менеджар пароляў Bitwarden At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. From adabc59c039a4e42ac96064de162628c4f2cc3d9 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 25 Oct 2024 04:33:04 -0700 Subject: [PATCH 080/126] show New password after removing a password (#11697) --- .../send-form/components/options/send-options.component.html | 2 +- .../src/send-form/components/options/send-options.component.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html index 98da24b5188..fe05cd71b94 100644 --- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html @@ -12,7 +12,7 @@

{{ "additionalOptions" | i18n }}

> - {{ "password" | i18n }} + {{ (passwordRemoved ? "newPassword" : "password") | i18n }} - -
- -
- {{ "noCollectionsInList" | i18n }} -
- - - - - - {{ c.name }} - - - - - - - - - - - diff --git a/apps/web/src/app/vault/individual-vault/collections.component.ts b/apps/web/src/app/vault/individual-vault/collections.component.ts deleted file mode 100644 index f527a74e368..00000000000 --- a/apps/web/src/app/vault/individual-vault/collections.component.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; -import { Component, Inject, OnDestroy } from "@angular/core"; - -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; -import { CollectionsComponent as BaseCollectionsComponent } from "@bitwarden/angular/admin-console/components/collections.component"; -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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { DialogService, ToastService } from "@bitwarden/components"; - -@Component({ - selector: "app-vault-collections", - templateUrl: "collections.component.html", -}) -export class CollectionsComponent extends BaseCollectionsComponent implements OnDestroy { - constructor( - collectionService: CollectionService, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - cipherService: CipherService, - organizationSerivce: OrganizationService, - logService: LogService, - accountService: AccountService, - protected dialogRef: DialogRef, - @Inject(DIALOG_DATA) params: CollectionsDialogParams, - toastService: ToastService, - ) { - super( - collectionService, - platformUtilsService, - i18nService, - cipherService, - organizationSerivce, - logService, - accountService, - toastService, - ); - this.cipherId = params?.cipherId; - } - - override async submit(): Promise { - const success = await super.submit(); - if (success) { - this.dialogRef.close(CollectionsDialogResult.Saved); - return true; - } - return false; - } - - check(c: CollectionView, select?: boolean) { - if (!c.canEditItems(this.organization)) { - return; - } - (c as any).checked = select == null ? !(c as any).checked : select; - } - - selectAll(select: boolean) { - this.collections.forEach((c) => this.check(c, select)); - } - - ngOnDestroy() { - this.selectAll(false); - } -} - -export interface CollectionsDialogParams { - cipherId: string; -} - -export enum CollectionsDialogResult { - Saved = "saved", -} - -/** - * Strongly typed helper to open a Collections dialog - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param config Optional configuration for the dialog - */ -export function openIndividualVaultCollectionsDialog( - dialogService: DialogService, - config?: DialogConfig, -) { - return dialogService.open( - CollectionsComponent, - config, - ); -} diff --git a/apps/web/src/app/vault/org-vault/collections.component.ts b/apps/web/src/app/vault/org-vault/collections.component.ts deleted file mode 100644 index 47f5325c756..00000000000 --- a/apps/web/src/app/vault/org-vault/collections.component.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; -import { Component, Inject } from "@angular/core"; - -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { 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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; -import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; -import { CipherCollectionsRequest } from "@bitwarden/common/vault/models/request/cipher-collections.request"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { - CollectionsComponent as BaseCollectionsComponent, - CollectionsDialogResult, -} from "../individual-vault/collections.component"; - -@Component({ - selector: "app-org-vault-collections", - templateUrl: "../../vault/individual-vault/collections.component.html", -}) -export class CollectionsComponent extends BaseCollectionsComponent { - organization: Organization; - - constructor( - collectionService: CollectionService, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - cipherService: CipherService, - organizationService: OrganizationService, - private apiService: ApiService, - logService: LogService, - accountService: AccountService, - protected dialogRef: DialogRef, - @Inject(DIALOG_DATA) params: OrgVaultCollectionsDialogParams, - toastService: ToastService, - ) { - super( - collectionService, - platformUtilsService, - i18nService, - cipherService, - organizationService, - logService, - accountService, - dialogRef, - params, - toastService, - ); - this.allowSelectNone = true; - this.collectionIds = params?.collectionIds; - this.collections = params?.collections; - this.organization = params?.organization; - this.cipherId = params?.cipherId; - } - - protected async loadCipher() { - // if cipher is unassigned use apiService. We can see this by looking at this.collectionIds - if (!this.organization.canEditAllCiphers && this.collectionIds.length !== 0) { - return await super.loadCipher(); - } - const response = await this.apiService.getCipherAdmin(this.cipherId); - return new Cipher(new CipherData(response)); - } - - protected loadCipherCollections() { - if (!this.organization.canViewAllCollections) { - return super.loadCipherCollections(); - } - return this.collectionIds; - } - - protected loadCollections() { - if (!this.organization.canViewAllCollections) { - return super.loadCollections(); - } - return Promise.resolve(this.collections); - } - - protected saveCollections() { - if (this.organization.canEditAllCiphers || this.collectionIds.length === 0) { - const request = new CipherCollectionsRequest(this.cipherDomain.collectionIds); - return this.apiService.putCipherCollectionsAdmin(this.cipherId, request); - } else { - return super.saveCollections(); - } - } -} - -export interface OrgVaultCollectionsDialogParams { - collectionIds: string[]; - collections: CollectionView[]; - organization: Organization; - cipherId: string; -} - -/** - * Strongly typed helper to open a Collections dialog - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param config Optional configuration for the dialog - */ -export function openOrgVaultCollectionsDialog( - dialogService: DialogService, - config?: DialogConfig, -) { - return dialogService.open( - CollectionsComponent, - config, - ); -} diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index 87e40036d50..4c7e35ef6f4 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -93,7 +93,6 @@ import { BulkDeleteDialogResult, openBulkDeleteDialog, } from "../individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component"; -import { CollectionsDialogResult } from "../individual-vault/collections.component"; import { RoutedVaultFilterBridgeService } from "../individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; import { RoutedVaultFilterService } from "../individual-vault/vault-filter/services/routed-vault-filter.service"; import { createFilterFunction } from "../individual-vault/vault-filter/shared/models/filter-function"; @@ -111,7 +110,6 @@ import { BulkCollectionsDialogResult, } from "./bulk-collections-dialog"; import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component"; -import { openOrgVaultCollectionsDialog } from "./collections.component"; import { AdminConsoleCipherFormConfigService } from "./services/admin-console-cipher-form-config.service"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; const BroadcasterSubscriptionId = "OrgVaultComponent"; @@ -718,39 +716,6 @@ export class VaultComponent implements OnInit, OnDestroy { }); } - async editCipherCollections(cipher: CipherView) { - let collections: CollectionAdminView[] = []; - - // Admins limited to only adding items to collections they have access to. - collections = await firstValueFrom( - this.allCollectionsWithoutUnassigned$.pipe( - map((c) => { - return c.sort((a, b) => { - if (a.canEditItems(this.organization) && !b.canEditItems(this.organization)) { - return -1; - } else if (!a.canEditItems(this.organization) && b.canEditItems(this.organization)) { - return 1; - } else { - return a.name.localeCompare(b.name); - } - }); - }), - ), - ); - const dialog = openOrgVaultCollectionsDialog(this.dialogService, { - data: { - collectionIds: cipher.collectionIds, - collections: collections, - organization: this.organization, - cipherId: cipher.id, - }, - }); - - if ((await lastValueFrom(dialog.closed)) == CollectionsDialogResult.Saved) { - this.refresh(); - } - } - async addCipher(cipherType?: CipherType) { if (this.extensionRefreshEnabled) { return this.addCipherV2(cipherType); From 2fb6a9e1eead16c4a454a6da1ba3b4937895dc20 Mon Sep 17 00:00:00 2001 From: Alec Rippberger <127791530+alec-livefront@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:58:02 -0500 Subject: [PATCH 088/126] [PM-13150] Remove uses of row-main Bootstrap class in Account Security Settings (#11650) * Remove uses of row-main Bootstrap class in Account Security Settings component * Remove class from new component * Remove extra div * Remove extra markup --- .../src/auth/popup/settings/account-security.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/auth/popup/settings/account-security.component.html b/apps/browser/src/auth/popup/settings/account-security.component.html index af6525daa8a..00e1fd17150 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.html +++ b/apps/browser/src/auth/popup/settings/account-security.component.html @@ -94,7 +94,7 @@

{{ "otherOptions" | i18n }}

@@ -115,11 +115,11 @@

{{ "otherOptions" | i18n }}

" > -
{{ "lockNow" | i18n }}
+ {{ "lockNow" | i18n }}
-
{{ "logOut" | i18n }}
+ {{ "logOut" | i18n }}
From 237887a368fbcc47c6db161a290950f5f01a9aa1 Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Fri, 25 Oct 2024 15:18:28 -0400 Subject: [PATCH 089/126] Show toast when copying SCIM api key and URL (#11608) --- .../organizations/manage/scim.component.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts index 76e3caa145f..c900cfe28a6 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts @@ -82,6 +82,11 @@ export class ScimComponent implements OnInit { copyScimUrl = async () => { this.platformUtilsService.copyToClipboard(await this.getScimEndpointUrl()); + this.toastService.showToast({ + message: this.i18nService.t("valueCopied", this.i18nService.t("scimUrl")), + variant: "success", + title: null, + }); }; rotateScimKey = async () => { @@ -114,6 +119,11 @@ export class ScimComponent implements OnInit { copyScimKey = async () => { this.platformUtilsService.copyToClipboard(this.formData.get("clientSecret").value); + this.toastService.showToast({ + message: this.i18nService.t("valueCopied", this.i18nService.t("scimApiKey")), + variant: "success", + title: null, + }); }; submit = async () => { From 418889446801d868c5fb1c40b277b8fcae01db96 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:04:22 -0700 Subject: [PATCH 090/126] [PM-13979] - add remaining components to access intelligence page (#11677) * add remaining components to access intelligence page * small css adjustments --- .../access-intelligence.component.html | 18 +++++++++++++++++- .../access-intelligence.component.ts | 15 ++++++++++++++- apps/web/src/locales/en/messages.json | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html index 78ddfb23929..44ca90cfaa5 100644 --- a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html +++ b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.html @@ -1,4 +1,20 @@ - +
{{ "accessIntelligence" | i18n }}
+

{{ "passwordRisk" | i18n }}

+
{{ "discoverAtRiskPasswords" | i18n }}
+
+ + {{ + "dataLastUpdated" | i18n: (dataLastUpdated | date: "MMMM d, y 'at' h:mm a") + }} + + {{ "refresh" | i18n }} + +
diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts index 3444e3a7ff1..90b2979d1b1 100644 --- a/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts +++ b/apps/web/src/app/tools/access-intelligence/access-intelligence.component.ts @@ -5,7 +5,7 @@ import { ActivatedRoute } from "@angular/router"; import { first } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { TabsModule } from "@bitwarden/components"; +import { AsyncActionsModule, ButtonModule, TabsModule } from "@bitwarden/components"; import { HeaderModule } from "../../layouts/header/header.module"; @@ -25,6 +25,8 @@ export enum AccessIntelligenceTabType { templateUrl: "./access-intelligence.component.html", imports: [ ApplicationTableComponent, + AsyncActionsModule, + ButtonModule, CommonModule, JslibModule, HeaderModule, @@ -36,11 +38,22 @@ export enum AccessIntelligenceTabType { }) export class AccessIntelligenceComponent { tabIndex: AccessIntelligenceTabType; + dataLastUpdated = new Date(); apps: any[] = []; priorityApps: any[] = []; notifiedMembers: any[] = []; + async refreshData() { + // TODO: Implement + return new Promise((resolve) => + setTimeout(() => { + this.dataLastUpdated = new Date(); + resolve(true); + }, 1000), + ); + } + constructor(route: ActivatedRoute) { route.queryParams.pipe(takeUntilDestroyed(), first()).subscribe(({ tabIndex }) => { this.tabIndex = !isNaN(tabIndex) ? tabIndex : AccessIntelligenceTabType.AllApps; diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 470852dce40..d1c5bd97f3b 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -8,6 +8,21 @@ "accessIntelligence": { "message": "Access Intelligence" }, + "passwordRisk": { + "message": "Password Risk" + }, + "discoverAtRiskPasswords": { + "message": "Discover at-risk passwords and notify users to change those passwords." + }, + "dataLastUpdated": { + "message": "Data last updated: $DATE$", + "placeholders": { + "date": { + "content": "$1", + "example": "2021-01-01" + } + } + }, "notifiedMembers": { "message": "Notified members" }, From ad7fa71e76d0b9175a340c160af94babd528f87f Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:04:36 -0700 Subject: [PATCH 091/126] [PM-13931] - UI - All applications report table (#11678) * add remaining components to access intelligence page * Revert "add remaining components to access intelligence page" This reverts commit cb8e8266561e32baa34a82241a05c5d38f1cdd97. * add remaining access intelligence table components --- .../no-priority-apps.component.html | 15 ++++++++ .../no-priority-apps.component.ts | 15 ++++++++ .../password-health-members.component.html | 28 ++++++++++++++- .../password-health-members.component.ts | 36 ++++++++++--------- .../password-health.component.html | 2 +- apps/web/src/locales/en/messages.json | 12 +++++++ libs/tools/card/src/card.component.ts | 2 +- 7 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 apps/web/src/app/tools/access-intelligence/no-priority-apps.component.html create mode 100644 apps/web/src/app/tools/access-intelligence/no-priority-apps.component.ts diff --git a/apps/web/src/app/tools/access-intelligence/no-priority-apps.component.html b/apps/web/src/app/tools/access-intelligence/no-priority-apps.component.html new file mode 100644 index 00000000000..9c3b4f48916 --- /dev/null +++ b/apps/web/src/app/tools/access-intelligence/no-priority-apps.component.html @@ -0,0 +1,15 @@ + + +

+ {{ "noPriorityApplicationsTitle" | i18n }} +

+
+ +

+ {{ "noPriorityApplicationsDescription" | i18n }} +

+
+ + + +
diff --git a/apps/web/src/app/tools/access-intelligence/no-priority-apps.component.ts b/apps/web/src/app/tools/access-intelligence/no-priority-apps.component.ts new file mode 100644 index 00000000000..e4e54ca2f13 --- /dev/null +++ b/apps/web/src/app/tools/access-intelligence/no-priority-apps.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, NoItemsModule, Icons } from "@bitwarden/components"; + +@Component({ + standalone: true, + selector: "tools-no-priority-apps", + templateUrl: "no-priority-apps.component.html", + imports: [ButtonModule, CommonModule, JslibModule, NoItemsModule], +}) +export class NoPriorityAppsComponent { + noItemsIcon = Icons.NoResults; +} diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members.component.html b/apps/web/src/app/tools/access-intelligence/password-health-members.component.html index f902011110b..885c21f0a2c 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health-members.component.html +++ b/apps/web/src/app/tools/access-intelligence/password-health-members.component.html @@ -8,7 +8,33 @@ > {{ "loading" | i18n }} -
+
+ +
+
+
+ + + + +
+
+ + +
diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts b/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts index 30c9ad8dba8..17b2456406b 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts +++ b/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts @@ -1,10 +1,9 @@ -import { CommonModule } from "@angular/common"; import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormControl, FormsModule } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { map } from "rxjs"; +import { debounceTime, map } from "rxjs"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; // eslint-disable-next-line no-restricted-imports import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; @@ -12,33 +11,31 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { - BadgeModule, - BadgeVariant, - ContainerComponent, - TableDataSource, - TableModule, -} from "@bitwarden/components"; +import { BadgeVariant, SearchModule, TableDataSource, TableModule } from "@bitwarden/components"; +import { CardComponent } from "@bitwarden/tools-card"; -// eslint-disable-next-line no-restricted-imports import { HeaderModule } from "../../layouts/header/header.module"; // eslint-disable-next-line no-restricted-imports +import { SharedModule } from "../../shared"; import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module"; // eslint-disable-next-line no-restricted-imports import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { NoPriorityAppsComponent } from "./no-priority-apps.component"; + @Component({ standalone: true, selector: "tools-password-health-members", templateUrl: "password-health-members.component.html", imports: [ - BadgeModule, + CardComponent, OrganizationBadgeModule, - CommonModule, - ContainerComponent, PipesModule, - JslibModule, HeaderModule, + SearchModule, + FormsModule, + NoPriorityAppsComponent, + SharedModule, TableModule, ], providers: [PasswordHealthService], @@ -56,6 +53,8 @@ export class PasswordHealthMembersComponent implements OnInit { loading = true; + protected searchControl = new FormControl("", { nonNullable: true }); + private destroyRef = inject(DestroyRef); constructor( @@ -64,7 +63,11 @@ export class PasswordHealthMembersComponent implements OnInit { protected auditService: AuditService, protected i18nService: I18nService, protected activatedRoute: ActivatedRoute, - ) {} + ) { + this.searchControl.valueChanges + .pipe(debounceTime(200), takeUntilDestroyed()) + .subscribe((v) => (this.dataSource.filter = v)); + } ngOnInit() { this.activatedRoute.paramMap @@ -89,6 +92,7 @@ export class PasswordHealthMembersComponent implements OnInit { await passwordHealthService.generateReport(); this.dataSource.data = passwordHealthService.reportCiphers; + this.exposedPasswordMap = passwordHealthService.exposedPasswordMap; this.passwordStrengthMap = passwordHealthService.passwordStrengthMap; this.passwordUseMap = passwordHealthService.passwordUseMap; diff --git a/apps/web/src/app/tools/access-intelligence/password-health.component.html b/apps/web/src/app/tools/access-intelligence/password-health.component.html index 32459706449..5b1fe4610d9 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health.component.html +++ b/apps/web/src/app/tools/access-intelligence/password-health.component.html @@ -8,7 +8,7 @@ > {{ "loading" | i18n }}
-
+
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index d1c5bd97f3b..9ea33149de8 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -53,6 +53,18 @@ } } }, + "noPriorityApplicationsTitle": { + "message": "You haven’t marked any applications as a priority" + }, + "noPriorityApplicationsDescription": { + "message": "Select your most critical applications to discover at-risk passwords, and notify users to change those passwords." + }, + "markPriorityApps": { + "message": "Mark priority apps" + }, + "markAppAsCritical": { + "message": "Mark app as critical" + }, "application": { "message": "Application" }, diff --git a/libs/tools/card/src/card.component.ts b/libs/tools/card/src/card.component.ts index 9305246c581..85db7eaa7b3 100644 --- a/libs/tools/card/src/card.component.ts +++ b/libs/tools/card/src/card.component.ts @@ -11,7 +11,7 @@ import { TypographyModule } from "@bitwarden/components"; imports: [CommonModule, TypographyModule, JslibModule], host: { class: - "tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-border [&:not(bit-layout_*)]:tw-rounded-lg tw-p-6", + "tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-border [&:not(bit-layout_*)]:tw-rounded-lg tw-rounded-lg tw-p-6", }, }) export class CardComponent { From 5f0755d74d90b773fc6e70d6ead5139c3691471f Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Fri, 25 Oct 2024 16:17:36 -0500 Subject: [PATCH 092/126] [PM-14045] Scrolling content outside of iframe bounds breaks inline menu position (#11716) * [PM-14045] Scrolling content outside of iframe bounds breaks inline menu position * [PM-14045] Scrolling content outside of iframe bounds breaks inline menu position * [PM-14045] Fixing jest test * [PM-14045] Adjusting how we determine if the inline menu should reposition on scroll --- .../abstractions/overlay.background.ts | 1 + .../autofill/background/overlay.background.ts | 16 ++++++++ .../autofill-overlay-content.service.spec.ts | 1 + .../autofill-overlay-content.service.ts | 37 ++++++++++++++++--- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 68d3f32b80f..db50b784453 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -216,6 +216,7 @@ export type OverlayBackgroundExtensionMessageHandlers = { getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number; updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; triggerSubFrameFocusInRebuild: ({ sender }: BackgroundSenderParam) => void; + shouldRepositionSubFrameInlineMenuOnScroll: ({ sender }: BackgroundSenderParam) => void; destroyAutofillInlineMenuListeners: ({ message, sender, diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 2b8f2c273c7..6fb4589baa1 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -168,6 +168,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender), updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender), triggerSubFrameFocusInRebuild: ({ sender }) => this.triggerSubFrameFocusInRebuild(sender), + shouldRepositionSubFrameInlineMenuOnScroll: ({ sender }) => + this.shouldRepositionSubFrameInlineMenuOnScroll(sender), destroyAutofillInlineMenuListeners: ({ message, sender }) => this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId), collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender), @@ -2594,6 +2596,20 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.repositionInlineMenu$.next(sender); } + /** + * Triggers on scroll of a frame within the tab. Will reposition the inline menu + * if the focused field is within a sub-frame and the inline menu is visible. + * + * @param sender - The sender of the message + */ + private shouldRepositionSubFrameInlineMenuOnScroll(sender: chrome.runtime.MessageSender) { + if (!this.isInlineMenuButtonVisible || sender.tab.id !== this.focusedFieldData?.tabId) { + return false; + } + + return this.focusedFieldData.frameId > 0; + } + /** * Handles determining if the inline menu should be repositioned or closed, and initiates * the process of calculating the new position of the inline menu. diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts index c74fa21937a..91ad63955c7 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts @@ -1699,6 +1699,7 @@ describe("AutofillOverlayContentService", () => { const repositionEvents = [EVENTS.SCROLL, EVENTS.RESIZE]; repositionEvents.forEach((repositionEvent) => { it(`sends a message trigger overlay reposition message to the background when a ${repositionEvent} event occurs`, async () => { + sendExtensionMessageSpy.mockResolvedValueOnce(true); globalThis.dispatchEvent(new Event(repositionEvent)); await flushPromises(); 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 760a585bd60..645795d9f27 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -1571,14 +1571,35 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ AUTOFILL_OVERLAY_HANDLE_REPOSITION, ); - const eventTargetDoesNotContainFocusedField = (element: Element) => - typeof element?.contains === "function" && !element.contains(this.mostRecentlyFocusedField); + const eventTargetContainsFocusedField = (eventTarget: Element | Document) => { + if (!eventTarget || !this.mostRecentlyFocusedField) { + return false; + } + + const activeElement = (eventTarget as Document).activeElement; + if (activeElement) { + return ( + activeElement === this.mostRecentlyFocusedField || + activeElement.contains(this.mostRecentlyFocusedField) + ); + } + + if (typeof eventTarget.contains !== "function") { + return false; + } + return ( + eventTarget === this.mostRecentlyFocusedField || + eventTarget.contains(this.mostRecentlyFocusedField) + ); + }; const scrollHandler = this.useEventHandlersMemo( - throttle((event) => { - if (eventTargetDoesNotContainFocusedField(event.target as Element)) { - return; + throttle(async (event) => { + if ( + eventTargetContainsFocusedField(event.target) || + (await this.shouldRepositionSubFrameInlineMenuOnScroll()) + ) { + repositionHandler(event); } - repositionHandler(event); }, 50), AUTOFILL_OVERLAY_HANDLE_SCROLL, ); @@ -1590,6 +1611,10 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ globalThis.addEventListener(EVENTS.RESIZE, repositionHandler); } + private shouldRepositionSubFrameInlineMenuOnScroll = async () => { + return await this.sendExtensionMessage("shouldRepositionSubFrameInlineMenuOnScroll"); + }; + /** * Removes the listeners that facilitate repositioning * the overlay elements on scroll or resize. From c780cbc77eb4dabeaeb32bd256b0c293f6083d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=A8=20Audrey=20=E2=9C=A8?= Date: Fri, 25 Oct 2024 17:43:49 -0400 Subject: [PATCH 093/126] use `valueLabel` with `appCopyClick` directive (#11718) * add `valueLabel` to `appCopyClick` directives * move generated value description to the configuration --- .../src/credential-generator.component.html | 1 + .../src/credential-generator.component.ts | 8 ++++++++ .../src/password-generator.component.html | 1 + .../components/src/password-generator.component.ts | 8 ++++++++ .../src/username-generator.component.html | 1 + .../components/src/username-generator.component.ts | 8 ++++++++ libs/tools/generator/core/src/data/generators.ts | 6 ++++++ .../services/credential-generator.service.spec.ts | 2 ++ .../src/services/credential-generator.service.ts | 1 + .../types/credential-generator-configuration.ts | 14 ++++++++++---- 10 files changed, 46 insertions(+), 4 deletions(-) diff --git a/libs/tools/generator/components/src/credential-generator.component.html b/libs/tools/generator/components/src/credential-generator.component.html index 737e32fa1f9..f580b75f1ba 100644 --- a/libs/tools/generator/components/src/credential-generator.component.html +++ b/libs/tools/generator/components/src/credential-generator.component.html @@ -32,6 +32,7 @@ showToast [appA11yTitle]="credentialTypeCopyLabel$ | async" [appCopyClick]="value$ | async" + [valueLabel]="credentialTypeLabel$ | async" >
diff --git a/libs/tools/generator/components/src/credential-generator.component.ts b/libs/tools/generator/components/src/credential-generator.component.ts index e800ce4bd39..579d196a7a6 100644 --- a/libs/tools/generator/components/src/credential-generator.component.ts +++ b/libs/tools/generator/components/src/credential-generator.component.ts @@ -468,6 +468,14 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy { map(({ generate }) => generate), ); + /** + * Emits the copy credential toast respective of the selected credential type + */ + protected credentialTypeLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ generatedValue }) => generatedValue), + ); + /** Emits hint key for the currently selected credential type */ protected credentialTypeHint$ = new ReplaySubject(1); diff --git a/libs/tools/generator/components/src/password-generator.component.html b/libs/tools/generator/components/src/password-generator.component.html index 96aa8f00b1c..6726df30855 100644 --- a/libs/tools/generator/components/src/password-generator.component.html +++ b/libs/tools/generator/components/src/password-generator.component.html @@ -30,6 +30,7 @@ showToast [appA11yTitle]="credentialTypeCopyLabel$ | async" [appCopyClick]="value$ | async" + [valueLabel]="credentialTypeLabel$ | async" >
diff --git a/libs/tools/generator/components/src/password-generator.component.ts b/libs/tools/generator/components/src/password-generator.component.ts index 60c3f629538..8566edf4664 100644 --- a/libs/tools/generator/components/src/password-generator.component.ts +++ b/libs/tools/generator/components/src/password-generator.component.ts @@ -233,6 +233,14 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy { map(({ generate }) => generate), ); + /** + * Emits the copy credential toast respective of the selected credential type + */ + protected credentialTypeLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ generatedValue }) => generatedValue), + ); + private toOptions(algorithms: AlgorithmInfo[]) { const options: Option[] = algorithms.map((algorithm) => ({ value: algorithm.id, diff --git a/libs/tools/generator/components/src/username-generator.component.html b/libs/tools/generator/components/src/username-generator.component.html index 18b29a6184b..36aaae57ce2 100644 --- a/libs/tools/generator/components/src/username-generator.component.html +++ b/libs/tools/generator/components/src/username-generator.component.html @@ -19,6 +19,7 @@ showToast [appA11yTitle]="credentialTypeCopyLabel$ | async" [appCopyClick]="value$ | async" + [valueLabel]="credentialTypeLabel$ | async" > {{ credentialTypeCopyLabel$ | async }} diff --git a/libs/tools/generator/components/src/username-generator.component.ts b/libs/tools/generator/components/src/username-generator.component.ts index 5187a313d10..6518ee51ed8 100644 --- a/libs/tools/generator/components/src/username-generator.component.ts +++ b/libs/tools/generator/components/src/username-generator.component.ts @@ -390,6 +390,14 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy { map(({ generate }) => generate), ); + /** + * Emits the copy credential toast respective of the selected credential type + */ + protected credentialTypeLabel$ = this.algorithm$.pipe( + filter((algorithm) => !!algorithm), + map(({ generatedValue }) => generatedValue), + ); + /** Emits hint key for the currently selected credential type */ protected credentialTypeHint$ = new ReplaySubject(1); diff --git a/libs/tools/generator/core/src/data/generators.ts b/libs/tools/generator/core/src/data/generators.ts index d86eb52a8fa..6090fe789cb 100644 --- a/libs/tools/generator/core/src/data/generators.ts +++ b/libs/tools/generator/core/src/data/generators.ts @@ -53,6 +53,7 @@ const PASSPHRASE = Object.freeze({ category: "password", nameKey: "passphrase", generateKey: "generatePassphrase", + generatedValueKey: "passphrase", copyKey: "copyPassphrase", onlyOnRequest: false, request: [], @@ -95,6 +96,7 @@ const PASSWORD = Object.freeze({ category: "password", nameKey: "password", generateKey: "generatePassword", + generatedValueKey: "password", copyKey: "copyPassword", onlyOnRequest: false, request: [], @@ -145,6 +147,7 @@ const USERNAME = Object.freeze({ category: "username", nameKey: "randomWord", generateKey: "generateUsername", + generatedValueKey: "username", copyKey: "copyUsername", onlyOnRequest: false, request: [], @@ -181,6 +184,7 @@ const CATCHALL = Object.freeze({ nameKey: "catchallEmail", descriptionKey: "catchallEmailDesc", generateKey: "generateEmail", + generatedValueKey: "email", copyKey: "copyEmail", onlyOnRequest: false, request: [], @@ -217,6 +221,7 @@ const SUBADDRESS = Object.freeze({ nameKey: "plusAddressedEmail", descriptionKey: "plusAddressedEmailDesc", generateKey: "generateEmail", + generatedValueKey: "email", copyKey: "copyEmail", onlyOnRequest: false, request: [], @@ -256,6 +261,7 @@ export function toCredentialGeneratorConfiguration Date: Sat, 26 Oct 2024 13:47:40 +0200 Subject: [PATCH 094/126] [PM-12607] Move key rotation to km ownership (#11709) * Move key rotation to km ownership * Fix build * Move userkey rotation data provider abstraction to km ownership * Move userkey rotation data provider abstraction to km ownership * Fix linting * Fix CODEOWNERS for key-management web * Remove incorrect export * Fix import error --- .github/CODEOWNERS | 2 +- .../organization-user-reset-password.service.ts | 3 +-- .../services/webauthn-login/webauthn-login-admin.service.ts | 3 ++- .../emergency-access/services/emergency-access.service.ts | 3 +-- apps/web/src/app/auth/settings/change-password.component.ts | 2 +- apps/web/src/app/auth/settings/settings.module.ts | 2 +- .../key-rotation/request/update-key.request.ts | 2 +- .../key-rotation/user-key-rotation-api.service.ts | 0 .../key-rotation/user-key-rotation.module.ts | 0 .../key-rotation/user-key-rotation.service.spec.ts | 0 .../key-rotation/user-key-rotation.service.ts | 4 ++-- .../migrate-legacy-encryption.component.html | 0 .../migrate-encryption/migrate-legacy-encryption.component.ts | 0 apps/web/src/app/oss-routing.module.ts | 2 +- libs/auth/src/common/abstractions/index.ts | 1 - .../src/tools/send/services/send.service.abstraction.ts | 2 +- libs/common/src/vault/abstractions/cipher.service.ts | 2 +- .../vault/abstractions/folder/folder.service.abstraction.ts | 2 +- .../user-key-rotation-data-provider.abstraction.ts | 0 libs/key-management/src/index.ts | 1 + 20 files changed, 15 insertions(+), 16 deletions(-) rename apps/web/src/app/{auth => key-management}/key-rotation/request/update-key.request.ts (88%) rename apps/web/src/app/{auth => key-management}/key-rotation/user-key-rotation-api.service.ts (100%) rename apps/web/src/app/{auth => key-management}/key-rotation/user-key-rotation.module.ts (100%) rename apps/web/src/app/{auth => key-management}/key-rotation/user-key-rotation.service.spec.ts (100%) rename apps/web/src/app/{auth => key-management}/key-rotation/user-key-rotation.service.ts (98%) rename apps/web/src/app/{auth => key-management}/migrate-encryption/migrate-legacy-encryption.component.html (100%) rename apps/web/src/app/{auth => key-management}/migrate-encryption/migrate-legacy-encryption.component.ts (100%) rename libs/{auth/src/common => key-management/src}/abstractions/user-key-rotation-data-provider.abstraction.ts (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 103401d1c97..5ba5885d725 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -106,7 +106,7 @@ apps/desktop/desktop_native @bitwarden/team-platform-dev ## Key management team files ## apps/desktop/src/key-management @bitwarden/team-key-management-dev -apps/web/src/key-management @bitwarden/team-key-management-dev +apps/web/src/app/key-management @bitwarden/team-key-management-dev apps/browser/src/key-management @bitwarden/team-key-management-dev apps/cli/src/key-management @bitwarden/team-key-management-dev libs/key-management @bitwarden/team-key-management-dev diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index abfae811d54..88cb75b087e 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -5,7 +5,6 @@ import { OrganizationUserResetPasswordRequest, OrganizationUserResetPasswordWithIdRequest, } from "@bitwarden/admin-console/common"; -import { UserKeyRotationDataProvider } from "@bitwarden/auth/common"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { @@ -21,7 +20,7 @@ import { EncryptedString, EncString } from "@bitwarden/common/platform/models/do import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; -import { KeyService } from "@bitwarden/key-management"; +import { UserKeyRotationDataProvider, KeyService } from "@bitwarden/key-management"; @Injectable({ providedIn: "root", diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts index 09ea17829ce..2edbd0ada1a 100644 --- a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts +++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts @@ -1,7 +1,7 @@ import { Injectable, Optional } from "@angular/core"; import { BehaviorSubject, filter, from, map, Observable, shareReplay, switchMap, tap } from "rxjs"; -import { PrfKeySet, UserKeyRotationDataProvider } from "@bitwarden/auth/common"; +import { PrfKeySet } from "@bitwarden/auth/common"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { WebAuthnLoginPrfKeyServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login-prf-key.service.abstraction"; import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request"; @@ -11,6 +11,7 @@ import { Verification } from "@bitwarden/common/auth/types/verification"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; +import { UserKeyRotationDataProvider } from "@bitwarden/key-management"; import { CredentialCreateOptionsView } from "../../views/credential-create-options.view"; import { PendingWebauthnLoginCredentialView } from "../../views/pending-webauthn-login-credential.view"; diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts index a4fd9eeff02..39eb6570df9 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts @@ -1,6 +1,5 @@ import { Injectable } from "@angular/core"; -import { UserKeyRotationDataProvider } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; @@ -23,7 +22,7 @@ import { UserKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { KeyService } from "@bitwarden/key-management"; +import { UserKeyRotationDataProvider, KeyService } from "@bitwarden/key-management"; import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type"; import { EmergencyAccessType } from "../enums/emergency-access-type"; diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index ec1416ba0ae..3406c2d5b61 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -25,7 +25,7 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { KeyService } from "@bitwarden/key-management"; -import { UserKeyRotationService } from "../key-rotation/user-key-rotation.service"; +import { UserKeyRotationService } from "../../key-management/key-rotation/user-key-rotation.service"; @Component({ selector: "app-change-password", diff --git a/apps/web/src/app/auth/settings/settings.module.ts b/apps/web/src/app/auth/settings/settings.module.ts index 2d1f64d1ebb..437711f4aa6 100644 --- a/apps/web/src/app/auth/settings/settings.module.ts +++ b/apps/web/src/app/auth/settings/settings.module.ts @@ -2,9 +2,9 @@ import { NgModule } from "@angular/core"; import { PasswordCalloutComponent } from "@bitwarden/auth/angular"; +import { UserKeyRotationModule } from "../../key-management/key-rotation/user-key-rotation.module"; import { SharedModule } from "../../shared"; import { EmergencyAccessModule } from "../emergency-access"; -import { UserKeyRotationModule } from "../key-rotation/user-key-rotation.module"; import { ChangePasswordComponent } from "./change-password.component"; import { WebauthnLoginSettingsModule } from "./webauthn-login-settings"; diff --git a/apps/web/src/app/auth/key-rotation/request/update-key.request.ts b/apps/web/src/app/key-management/key-rotation/request/update-key.request.ts similarity index 88% rename from apps/web/src/app/auth/key-rotation/request/update-key.request.ts rename to apps/web/src/app/key-management/key-rotation/request/update-key.request.ts index 0988ed54a99..81b7d361579 100644 --- a/apps/web/src/app/auth/key-rotation/request/update-key.request.ts +++ b/apps/web/src/app/key-management/key-rotation/request/update-key.request.ts @@ -4,7 +4,7 @@ import { SendWithIdRequest } from "@bitwarden/common/src/tools/send/models/reque import { CipherWithIdRequest } from "@bitwarden/common/src/vault/models/request/cipher-with-id.request"; import { FolderWithIdRequest } from "@bitwarden/common/src/vault/models/request/folder-with-id.request"; -import { EmergencyAccessWithIdRequest } from "../../emergency-access/request/emergency-access-update.request"; +import { EmergencyAccessWithIdRequest } from "../../../auth/emergency-access/request/emergency-access-update.request"; export class UpdateKeyRequest { masterPasswordHash: string; diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation-api.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation-api.service.ts similarity index 100% rename from apps/web/src/app/auth/key-rotation/user-key-rotation-api.service.ts rename to apps/web/src/app/key-management/key-rotation/user-key-rotation-api.service.ts diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.module.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.module.ts similarity index 100% rename from apps/web/src/app/auth/key-rotation/user-key-rotation.module.ts rename to apps/web/src/app/key-management/key-rotation/user-key-rotation.module.ts diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts similarity index 100% rename from apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts rename to apps/web/src/app/key-management/key-rotation/user-key-rotation.service.spec.ts diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts similarity index 98% rename from apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts rename to apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index 8116bcd0470..e4e5ab2caaa 100644 --- a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -18,8 +18,8 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { KeyService } from "@bitwarden/key-management"; import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service"; -import { WebauthnLoginAdminService } from "../core"; -import { EmergencyAccessService } from "../emergency-access"; +import { WebauthnLoginAdminService } from "../../auth/core"; +import { EmergencyAccessService } from "../../auth/emergency-access"; import { UpdateKeyRequest } from "./request/update-key.request"; import { UserKeyRotationApiService } from "./user-key-rotation-api.service"; diff --git a/apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.html b/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.html similarity index 100% rename from apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.html rename to apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.html diff --git a/apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.ts b/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts similarity index 100% rename from apps/web/src/app/auth/migrate-encryption/migrate-legacy-encryption.component.ts rename to apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 9f36df175fe..b3a8db20028 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -177,7 +177,7 @@ const routes: Routes = [ { path: "migrate-legacy-encryption", loadComponent: () => - import("./auth/migrate-encryption/migrate-legacy-encryption.component").then( + import("./key-management/migrate-encryption/migrate-legacy-encryption.component").then( (mod) => mod.MigrateFromLegacyEncryptionComponent, ), }, diff --git a/libs/auth/src/common/abstractions/index.ts b/libs/auth/src/common/abstractions/index.ts index 6b618992e9d..e686de52013 100644 --- a/libs/auth/src/common/abstractions/index.ts +++ b/libs/auth/src/common/abstractions/index.ts @@ -3,4 +3,3 @@ export * from "./login-email.service"; export * from "./login-strategy.service"; export * from "./user-decryption-options.service.abstraction"; export * from "./auth-request.service.abstraction"; -export * from "./user-key-rotation-data-provider.abstraction"; diff --git a/libs/common/src/tools/send/services/send.service.abstraction.ts b/libs/common/src/tools/send/services/send.service.abstraction.ts index 4fa927942c1..866a661b4a4 100644 --- a/libs/common/src/tools/send/services/send.service.abstraction.ts +++ b/libs/common/src/tools/send/services/send.service.abstraction.ts @@ -1,6 +1,6 @@ import { Observable } from "rxjs"; -import { UserKeyRotationDataProvider } from "@bitwarden/auth/common"; +import { UserKeyRotationDataProvider } from "@bitwarden/key-management"; import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; diff --git a/libs/common/src/vault/abstractions/cipher.service.ts b/libs/common/src/vault/abstractions/cipher.service.ts index f0e19a21342..444c922fe31 100644 --- a/libs/common/src/vault/abstractions/cipher.service.ts +++ b/libs/common/src/vault/abstractions/cipher.service.ts @@ -1,7 +1,7 @@ import { Observable } from "rxjs"; -import { UserKeyRotationDataProvider } from "@bitwarden/auth/common"; import { LocalData } from "@bitwarden/common/vault/models/data/local.data"; +import { UserKeyRotationDataProvider } from "@bitwarden/key-management"; import { UriMatchStrategySetting } from "../../models/domain/domain-service"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; diff --git a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts index 857915ddb80..df21b136f41 100644 --- a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts +++ b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts @@ -1,6 +1,6 @@ import { Observable } from "rxjs"; -import { UserKeyRotationDataProvider } from "@bitwarden/auth/common"; +import { UserKeyRotationDataProvider } from "@bitwarden/key-management"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { UserId } from "../../../types/guid"; diff --git a/libs/auth/src/common/abstractions/user-key-rotation-data-provider.abstraction.ts b/libs/key-management/src/abstractions/user-key-rotation-data-provider.abstraction.ts similarity index 100% rename from libs/auth/src/common/abstractions/user-key-rotation-data-provider.abstraction.ts rename to libs/key-management/src/abstractions/user-key-rotation-data-provider.abstraction.ts diff --git a/libs/key-management/src/index.ts b/libs/key-management/src/index.ts index f2bb5e30166..5ad96ddeba7 100644 --- a/libs/key-management/src/index.ts +++ b/libs/key-management/src/index.ts @@ -7,3 +7,4 @@ export * from "./biometrics/biometric.state"; export { KeyService } from "./abstractions/key.service"; export { DefaultKeyService } from "./key.service"; +export { UserKeyRotationDataProvider } from "./abstractions/user-key-rotation-data-provider.abstraction"; From d0ed9aaa5d3afbc2af5fc0872b6b1471f7c32f3b Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:13:32 +1000 Subject: [PATCH 095/126] [PM-13779] Add vNext CollectionService without ActiveUserState (#11705) - add tests - install jest-extended for additional matchers - allow for generation of different crypto keys in tests --- .../abstractions/vnext-collection.service.ts | 41 +++ .../default-vnext-collection.service.spec.ts | 325 ++++++++++++++++++ .../default-vnext-collection.service.ts | 196 +++++++++++ .../services/vnext-collection.state.ts | 37 ++ libs/common/spec/matchers/index.ts | 5 + libs/common/spec/utils.ts | 11 +- package-lock.json | 22 ++ package.json | 1 + 8 files changed, 636 insertions(+), 2 deletions(-) create mode 100644 libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts create mode 100644 libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts create mode 100644 libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts create mode 100644 libs/admin-console/src/common/collections/services/vnext-collection.state.ts diff --git a/libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts b/libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts new file mode 100644 index 00000000000..4b5828ccf3b --- /dev/null +++ b/libs/admin-console/src/common/collections/abstractions/vnext-collection.service.ts @@ -0,0 +1,41 @@ +import { Observable } from "rxjs"; + +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; + +import { CollectionData, Collection, CollectionView } from "../models"; + +export abstract class vNextCollectionService { + encryptedCollections$: (userId$: Observable) => Observable; + decryptedCollections$: (userId$: Observable) => Observable; + upsert: (collection: CollectionData | CollectionData[], userId: UserId) => Promise; + replace: (collections: { [id: string]: CollectionData }, userId: UserId) => Promise; + /** + * Clear decrypted state without affecting encrypted state. + * Used for locking the vault. + */ + clearDecryptedState: (userId: UserId) => Promise; + /** + * Clear decrypted and encrypted state. + * Used for logging out. + */ + clear: (userId: string) => Promise; + delete: (id: string | string[], userId: UserId) => Promise; + encrypt: (model: CollectionView) => Promise; + /** + * @deprecated This method will soon be made private, use `decryptedCollections$` instead. + */ + decryptMany: ( + collections: Collection[], + orgKeys?: Record, + ) => Promise; + /** + * Transforms the input CollectionViews into TreeNodes + */ + getAllNested: (collections: CollectionView[]) => TreeNode[]; + /** + * Transforms the input CollectionViews into TreeNodes and then returns the Treenode with the specified id + */ + getNested: (collections: CollectionView[], id: string) => TreeNode; +} diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts new file mode 100644 index 00000000000..4ca60cba77a --- /dev/null +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts @@ -0,0 +1,325 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { firstValueFrom, of, ReplaySubject } from "rxjs"; + +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { + FakeStateProvider, + makeEncString, + makeSymmetricCryptoKey, + mockAccountServiceWith, +} from "@bitwarden/common/spec"; +import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; + +import { CollectionData } from "../models"; + +import { DefaultvNextCollectionService } from "./default-vnext-collection.service"; +import { ENCRYPTED_COLLECTION_DATA_KEY } from "./vnext-collection.state"; + +describe("DefaultvNextCollectionService", () => { + let keyService: MockProxy; + let encryptService: MockProxy; + let i18nService: MockProxy; + let stateProvider: FakeStateProvider; + + let userId: UserId; + + let cryptoKeys: ReplaySubject | null>; + + let collectionService: DefaultvNextCollectionService; + + beforeEach(() => { + userId = Utils.newGuid() as UserId; + + keyService = mock(); + encryptService = mock(); + i18nService = mock(); + stateProvider = new FakeStateProvider(mockAccountServiceWith(userId)); + + cryptoKeys = new ReplaySubject(1); + keyService.orgKeys$.mockReturnValue(cryptoKeys); + + // Set up mock decryption + encryptService.decryptToUtf8 + .calledWith(expect.any(EncString), expect.any(SymmetricCryptoKey)) + .mockImplementation((encString, key) => + Promise.resolve(encString.data.replace("ENC_", "DEC_")), + ); + + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); + + // Arrange i18nService so that sorting algorithm doesn't throw + i18nService.collator = null; + + collectionService = new DefaultvNextCollectionService( + keyService, + encryptService, + i18nService, + stateProvider, + ); + }); + + afterEach(() => { + delete (window as any).bitwardenContainerService; + }); + + describe("decryptedCollections$", () => { + it("emits decrypted collections from state", async () => { + // Arrange test data + const org1 = Utils.newGuid() as OrganizationId; + const orgKey1 = makeSymmetricCryptoKey(64, 1); + const collection1 = collectionDataFactory(org1); + + const org2 = Utils.newGuid() as OrganizationId; + const orgKey2 = makeSymmetricCryptoKey(64, 2); + const collection2 = collectionDataFactory(org2); + + // Arrange dependencies + await setEncryptedState([collection1, collection2]); + cryptoKeys.next({ + [org1]: orgKey1, + [org2]: orgKey2, + }); + + const result = await firstValueFrom(collectionService.decryptedCollections$(of(userId))); + + // Assert emitted values + expect(result.length).toBe(2); + expect(result).toIncludeAllPartialMembers([ + { + id: collection1.id, + name: "DEC_NAME_" + collection1.id, + }, + { + id: collection2.id, + name: "DEC_NAME_" + collection2.id, + }, + ]); + + // Assert that the correct org keys were used for each encrypted string + expect(encryptService.decryptToUtf8).toHaveBeenCalledWith( + expect.objectContaining(new EncString(collection1.name)), + orgKey1, + ); + expect(encryptService.decryptToUtf8).toHaveBeenCalledWith( + expect.objectContaining(new EncString(collection2.name)), + orgKey2, + ); + }); + + it("handles null collection state", async () => { + // Arrange dependencies + await setEncryptedState(null); + cryptoKeys.next({}); + + const encryptedCollections = await firstValueFrom( + collectionService.encryptedCollections$(of(userId)), + ); + + expect(encryptedCollections.length).toBe(0); + }); + }); + + describe("encryptedCollections$", () => { + it("emits encrypted collections from state", async () => { + // Arrange test data + const collection1 = collectionDataFactory(); + const collection2 = collectionDataFactory(); + + // Arrange dependencies + await setEncryptedState([collection1, collection2]); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + + expect(result.length).toBe(2); + expect(result).toIncludeAllPartialMembers([ + { + id: collection1.id, + name: makeEncString("ENC_NAME_" + collection1.id), + }, + { + id: collection2.id, + name: makeEncString("ENC_NAME_" + collection2.id), + }, + ]); + }); + + it("handles null collection state", async () => { + await setEncryptedState(null); + + const decryptedCollections = await firstValueFrom( + collectionService.encryptedCollections$(of(userId)), + ); + expect(decryptedCollections.length).toBe(0); + }); + }); + + describe("upsert", () => { + it("upserts to existing collections", async () => { + const collection1 = collectionDataFactory(); + const collection2 = collectionDataFactory(); + + await setEncryptedState([collection1, collection2]); + + const updatedCollection1 = Object.assign(new CollectionData({} as any), collection1, { + name: makeEncString("UPDATED_ENC_NAME_" + collection1.id).encryptedString, + }); + const newCollection3 = collectionDataFactory(); + + await collectionService.upsert([updatedCollection1, newCollection3], userId); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + expect(result.length).toBe(3); + expect(result).toIncludeAllPartialMembers([ + { + id: collection1.id, + name: makeEncString("UPDATED_ENC_NAME_" + collection1.id), + }, + { + id: collection2.id, + name: makeEncString("ENC_NAME_" + collection2.id), + }, + { + id: newCollection3.id, + name: makeEncString("ENC_NAME_" + newCollection3.id), + }, + ]); + }); + + it("upserts to a null state", async () => { + const collection1 = collectionDataFactory(); + + await setEncryptedState(null); + + await collectionService.upsert(collection1, userId); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + expect(result.length).toBe(1); + expect(result).toIncludeAllPartialMembers([ + { + id: collection1.id, + name: makeEncString("ENC_NAME_" + collection1.id), + }, + ]); + }); + }); + + describe("replace", () => { + it("replaces all collections", async () => { + await setEncryptedState([collectionDataFactory(), collectionDataFactory()]); + + const newCollection3 = collectionDataFactory(); + await collectionService.replace( + { + [newCollection3.id]: newCollection3, + }, + userId, + ); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + expect(result.length).toBe(1); + expect(result).toIncludeAllPartialMembers([ + { + id: newCollection3.id, + name: makeEncString("ENC_NAME_" + newCollection3.id), + }, + ]); + }); + }); + + it("clearDecryptedState", async () => { + await setEncryptedState([collectionDataFactory(), collectionDataFactory()]); + + await collectionService.clearDecryptedState(userId); + + // Encrypted state remains + const encryptedState = await firstValueFrom( + collectionService.encryptedCollections$(of(userId)), + ); + expect(encryptedState.length).toEqual(2); + + // Decrypted state is cleared + const decryptedState = await firstValueFrom( + collectionService.decryptedCollections$(of(userId)), + ); + expect(decryptedState.length).toEqual(0); + }); + + it("clear", async () => { + await setEncryptedState([collectionDataFactory(), collectionDataFactory()]); + cryptoKeys.next({}); + + await collectionService.clear(userId); + + // Encrypted state is cleared + const encryptedState = await firstValueFrom( + collectionService.encryptedCollections$(of(userId)), + ); + expect(encryptedState.length).toEqual(0); + + // Decrypted state is cleared + const decryptedState = await firstValueFrom( + collectionService.decryptedCollections$(of(userId)), + ); + expect(decryptedState.length).toEqual(0); + }); + + describe("delete", () => { + it("deletes a collection", async () => { + const collection1 = collectionDataFactory(); + const collection2 = collectionDataFactory(); + await setEncryptedState([collection1, collection2]); + + await collectionService.delete(collection1.id, userId); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + expect(result.length).toEqual(1); + expect(result[0]).toMatchObject({ id: collection2.id }); + }); + + it("deletes several collections", async () => { + const collection1 = collectionDataFactory(); + const collection2 = collectionDataFactory(); + const collection3 = collectionDataFactory(); + await setEncryptedState([collection1, collection2, collection3]); + + await collectionService.delete([collection1.id, collection3.id], userId); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + expect(result.length).toEqual(1); + expect(result[0]).toMatchObject({ id: collection2.id }); + }); + + it("handles null collections", async () => { + const collection1 = collectionDataFactory(); + await setEncryptedState(null); + + await collectionService.delete(collection1.id, userId); + + const result = await firstValueFrom(collectionService.encryptedCollections$(of(userId))); + expect(result.length).toEqual(0); + }); + }); + + const setEncryptedState = (collectionData: CollectionData[] | null) => + stateProvider.setUserState( + ENCRYPTED_COLLECTION_DATA_KEY, + collectionData == null ? null : Object.fromEntries(collectionData.map((c) => [c.id, c])), + userId, + ); +}); + +const collectionDataFactory = (orgId?: OrganizationId) => { + const collection = new CollectionData({} as any); + collection.id = Utils.newGuid() as CollectionId; + collection.organizationId = orgId ?? (Utils.newGuid() as OrganizationId); + collection.name = makeEncString("ENC_NAME_" + collection.id).encryptedString; + + return collection; +}; diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts new file mode 100644 index 00000000000..8ca1ab7fcf0 --- /dev/null +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.ts @@ -0,0 +1,196 @@ +import { combineLatest, firstValueFrom, map, Observable, of, switchMap } from "rxjs"; + +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { StateProvider, DerivedState } from "@bitwarden/common/platform/state"; +import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; +import { KeyService } from "@bitwarden/key-management"; + +import { vNextCollectionService } from "../abstractions/vnext-collection.service"; +import { Collection, CollectionData, CollectionView } from "../models"; + +import { + DECRYPTED_COLLECTION_DATA_KEY, + ENCRYPTED_COLLECTION_DATA_KEY, +} from "./vnext-collection.state"; + +const NestingDelimiter = "/"; + +export class DefaultvNextCollectionService implements vNextCollectionService { + constructor( + private keyService: KeyService, + private encryptService: EncryptService, + private i18nService: I18nService, + protected stateProvider: StateProvider, + ) {} + + encryptedCollections$(userId$: Observable) { + return userId$.pipe( + switchMap((userId) => this.encryptedState(userId).state$), + map((collections) => { + if (collections == null) { + return []; + } + + return Object.values(collections).map((c) => new Collection(c)); + }), + ); + } + + decryptedCollections$(userId$: Observable) { + return userId$.pipe( + switchMap((userId) => this.decryptedState(userId).state$), + map((collections) => collections ?? []), + ); + } + + async upsert(toUpdate: CollectionData | CollectionData[], userId: UserId): Promise { + if (toUpdate == null) { + return; + } + await this.encryptedState(userId).update((collections) => { + if (collections == null) { + collections = {}; + } + if (Array.isArray(toUpdate)) { + toUpdate.forEach((c) => { + collections[c.id] = c; + }); + } else { + collections[toUpdate.id] = toUpdate; + } + return collections; + }); + } + + async replace(collections: Record, userId: UserId): Promise { + await this.encryptedState(userId).update(() => collections); + } + + async clearDecryptedState(userId: UserId): Promise { + if (userId == null) { + throw new Error("User ID is required."); + } + + await this.decryptedState(userId).forceValue(null); + } + + async clear(userId: UserId): Promise { + await this.encryptedState(userId).update(() => null); + // This will propagate from the encrypted state update, but by doing it explicitly + // the promise doesn't resolve until the update is complete. + await this.decryptedState(userId).forceValue(null); + } + + async delete(id: CollectionId | CollectionId[], userId: UserId): Promise { + await this.encryptedState(userId).update((collections) => { + if (collections == null) { + collections = {}; + } + if (typeof id === "string") { + delete collections[id]; + } else { + (id as CollectionId[]).forEach((i) => { + delete collections[i]; + }); + } + return collections; + }); + } + + async encrypt(model: CollectionView): Promise { + if (model.organizationId == null) { + throw new Error("Collection has no organization id."); + } + const key = await this.keyService.getOrgKey(model.organizationId); + if (key == null) { + throw new Error("No key for this collection's organization."); + } + const collection = new Collection(); + collection.id = model.id; + collection.organizationId = model.organizationId; + collection.readOnly = model.readOnly; + collection.externalId = model.externalId; + collection.name = await this.encryptService.encrypt(model.name, key); + return collection; + } + + // TODO: this should be private and orgKeys should be required. + // See https://bitwarden.atlassian.net/browse/PM-12375 + async decryptMany( + collections: Collection[], + orgKeys?: Record, + ): Promise { + if (collections == null || collections.length === 0) { + return []; + } + const decCollections: CollectionView[] = []; + + orgKeys ??= await firstValueFrom(this.keyService.activeUserOrgKeys$); + + const promises: Promise[] = []; + collections.forEach((collection) => { + promises.push( + collection + .decrypt(orgKeys[collection.organizationId as OrganizationId]) + .then((c) => decCollections.push(c)), + ); + }); + await Promise.all(promises); + return decCollections.sort(Utils.getSortFunction(this.i18nService, "name")); + } + + getAllNested(collections: CollectionView[]): TreeNode[] { + const nodes: TreeNode[] = []; + collections.forEach((c) => { + const collectionCopy = new CollectionView(); + collectionCopy.id = c.id; + collectionCopy.organizationId = c.organizationId; + const parts = c.name != null ? c.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : []; + ServiceUtils.nestedTraverse(nodes, 0, parts, collectionCopy, null, NestingDelimiter); + }); + return nodes; + } + + /** + * @deprecated August 30 2022: Moved to new Vault Filter Service + * Remove when Desktop and Browser are updated + */ + getNested(collections: CollectionView[], id: string): TreeNode { + const nestedCollections = this.getAllNested(collections); + return ServiceUtils.getTreeNodeObjectFromList( + nestedCollections, + id, + ) as TreeNode; + } + + /** + * @returns a SingleUserState for encrypted collection data. + */ + private encryptedState(userId: UserId) { + return this.stateProvider.getUser(userId, ENCRYPTED_COLLECTION_DATA_KEY); + } + + /** + * @returns a SingleUserState for decrypted collection data. + */ + private decryptedState(userId: UserId): DerivedState { + const encryptedCollectionsWithKeys = this.encryptedState(userId).combinedState$.pipe( + switchMap(([userId, collectionData]) => + combineLatest([of(collectionData), this.keyService.orgKeys$(userId)]), + ), + ); + + return this.stateProvider.getDerived( + encryptedCollectionsWithKeys, + DECRYPTED_COLLECTION_DATA_KEY, + { + collectionService: this, + }, + ); + } +} diff --git a/libs/admin-console/src/common/collections/services/vnext-collection.state.ts b/libs/admin-console/src/common/collections/services/vnext-collection.state.ts new file mode 100644 index 00000000000..533308f3cc7 --- /dev/null +++ b/libs/admin-console/src/common/collections/services/vnext-collection.state.ts @@ -0,0 +1,37 @@ +import { Jsonify } from "type-fest"; + +import { + COLLECTION_DATA, + DeriveDefinition, + UserKeyDefinition, +} from "@bitwarden/common/platform/state"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; + +import { vNextCollectionService } from "../abstractions/vnext-collection.service"; +import { Collection, CollectionData, CollectionView } from "../models"; + +export const ENCRYPTED_COLLECTION_DATA_KEY = UserKeyDefinition.record( + COLLECTION_DATA, + "collections", + { + deserializer: (jsonData: Jsonify) => CollectionData.fromJSON(jsonData), + clearOn: ["logout"], + }, +); + +export const DECRYPTED_COLLECTION_DATA_KEY = new DeriveDefinition< + [Record, Record], + CollectionView[], + { collectionService: vNextCollectionService } +>(COLLECTION_DATA, "decryptedCollections", { + deserializer: (obj) => obj.map((collection) => CollectionView.fromJSON(collection)), + derive: async ([collections, orgKeys], { collectionService }) => { + if (collections == null) { + return []; + } + + const data = Object.values(collections).map((c) => new Collection(c)); + return await collectionService.decryptMany(data, orgKeys); + }, +}); diff --git a/libs/common/spec/matchers/index.ts b/libs/common/spec/matchers/index.ts index 235f54d7754..44440be5b54 100644 --- a/libs/common/spec/matchers/index.ts +++ b/libs/common/spec/matchers/index.ts @@ -1,3 +1,5 @@ +import * as matchers from "jest-extended"; + import { toBeFulfilled, toBeResolved, toBeRejected } from "./promise-fulfilled"; import { toAlmostEqual } from "./to-almost-equal"; import { toEqualBuffer } from "./to-equal-buffer"; @@ -6,6 +8,9 @@ export * from "./to-equal-buffer"; export * from "./to-almost-equal"; export * from "./promise-fulfilled"; +// add all jest-extended matchers +expect.extend(matchers); + export function addCustomMatchers() { expect.extend({ toEqualBuffer: toEqualBuffer, diff --git a/libs/common/spec/utils.ts b/libs/common/spec/utils.ts index d3722329370..1cead2aa624 100644 --- a/libs/common/spec/utils.ts +++ b/libs/common/spec/utils.ts @@ -46,8 +46,15 @@ export function makeStaticByteArray(length: number, start = 0) { return arr; } -export function makeSymmetricCryptoKey(length: 32 | 64 = 64) { - return new SymmetricCryptoKey(makeStaticByteArray(length)) as T; +/** + * Creates a symmetric crypto key for use in tests. This is deterministic, i.e. it will produce identical keys + * for identical argument values. Provide a unique value to the `seed` parameter to create different keys. + */ +export function makeSymmetricCryptoKey( + length: 32 | 64 = 64, + seed = 0, +) { + return new SymmetricCryptoKey(makeStaticByteArray(length, seed)) as T; } /** diff --git a/package-lock.json b/package-lock.json index 344cf7835e1..f60ec732132 100644 --- a/package-lock.json +++ b/package-lock.json @@ -154,6 +154,7 @@ "html-webpack-injector": "1.1.4", "html-webpack-plugin": "5.6.0", "husky": "9.1.4", + "jest-extended": "^4.0.2", "jest-junit": "16.0.0", "jest-mock-extended": "3.0.7", "jest-preset-angular": "14.1.1", @@ -23930,6 +23931,27 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-extended": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-4.0.2.tgz", + "integrity": "sha512-FH7aaPgtGYHc9mRjriS0ZEHYM5/W69tLrFTIdzm+yJgeoCmmrSB/luSfMSqWP9O29QWHPEmJ4qmU6EwsZideog==", + "dev": true, + "dependencies": { + "jest-diff": "^29.0.0", + "jest-get-type": "^29.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "jest": ">=27.2.5" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + } + } + }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", diff --git a/package.json b/package.json index 75e91f64936..bf9c8b76735 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "html-webpack-injector": "1.1.4", "html-webpack-plugin": "5.6.0", "husky": "9.1.4", + "jest-extended": "^4.0.2", "jest-junit": "16.0.0", "jest-mock-extended": "3.0.7", "jest-preset-angular": "14.1.1", From 903c215867d37813fc3dbf70e5bdb04eef995dd1 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:02:57 +1000 Subject: [PATCH 096/126] Fix vNextCollectiuonService tests (#11739) --- .../services/default-vnext-collection.service.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts index 4ca60cba77a..54c4470d414 100644 --- a/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-vnext-collection.service.spec.ts @@ -47,7 +47,7 @@ describe("DefaultvNextCollectionService", () => { // Set up mock decryption encryptService.decryptToUtf8 - .calledWith(expect.any(EncString), expect.any(SymmetricCryptoKey)) + .calledWith(expect.any(EncString), expect.any(SymmetricCryptoKey), expect.any(String)) .mockImplementation((encString, key) => Promise.resolve(encString.data.replace("ENC_", "DEC_")), ); @@ -106,10 +106,12 @@ describe("DefaultvNextCollectionService", () => { expect(encryptService.decryptToUtf8).toHaveBeenCalledWith( expect.objectContaining(new EncString(collection1.name)), orgKey1, + expect.any(String), ); expect(encryptService.decryptToUtf8).toHaveBeenCalledWith( expect.objectContaining(new EncString(collection2.name)), orgKey2, + expect.any(String), ); }); From e3f903556457bb491309337792fb142fc42b9d3d Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Mon, 28 Oct 2024 02:17:15 -0700 Subject: [PATCH 097/126] [PM-14022] - change send link to disabled (#11721) * change send link to disabled * change send link to disabled --- .../components/send-details/send-details.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html index 93db4df3187..06b0f1a55df 100644 --- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html +++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html @@ -23,7 +23,7 @@

{{ "sendDetails" | i18n }}

{{ "sendLink" | i18n }} - + - - - + + + - - {{ "cancel" | i18n }} - -
- - - - - diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index b3a8db20028..71d26030b03 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -40,7 +40,6 @@ import { flagEnabled, Flags } from "../utils/flags"; import { VerifyRecoverDeleteOrgComponent } from "./admin-console/organizations/manage/verify-recover-delete-org.component"; import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component"; import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component"; -import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component"; import { CreateOrganizationComponent } from "./admin-console/settings/create-organization.component"; import { deepLinkGuard } from "./auth/guards/deep-link.guard"; import { HintComponent } from "./auth/hint.component"; @@ -156,12 +155,6 @@ const routes: Routes = [ canActivate: [unauthGuardFn()], data: { titleId: "deleteOrganization" }, }, - { - path: "verify-recover-delete-provider", - component: VerifyRecoverDeleteProviderComponent, - canActivate: [unauthGuardFn()], - data: { titleId: "deleteAccount" } satisfies RouteDataProperties, - }, { path: "update-temp-password", component: UpdateTempPasswordComponent, diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 638a523cd4f..a238f2110ce 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -17,8 +17,6 @@ import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } from "../admin-console/organizations/tools/reused-passwords-report.component"; import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../admin-console/organizations/tools/unsecured-websites-report.component"; import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../admin-console/organizations/tools/weak-passwords-report.component"; -import { ProvidersComponent } from "../admin-console/providers/providers.component"; -import { VerifyRecoverDeleteProviderComponent } from "../admin-console/providers/verify-recover-delete-provider.component"; import { HintComponent } from "../auth/hint.component"; import { RecoverDeleteComponent } from "../auth/recover-delete.component"; import { RecoverTwoFactorComponent } from "../auth/recover-two-factor.component"; @@ -149,7 +147,6 @@ import { SharedModule } from "./shared.module"; PremiumBadgeComponent, ProfileComponent, ChangeAvatarDialogComponent, - ProvidersComponent, PurgeVaultComponent, RecoverDeleteComponent, RecoverTwoFactorComponent, @@ -176,7 +173,6 @@ import { SharedModule } from "./shared.module"; UpdateTempPasswordComponent, VerifyEmailTokenComponent, VerifyRecoverDeleteComponent, - VerifyRecoverDeleteProviderComponent, ], exports: [ UserVerificationModule, @@ -218,7 +214,6 @@ import { SharedModule } from "./shared.module"; PremiumBadgeComponent, ProfileComponent, ChangeAvatarDialogComponent, - ProvidersComponent, PurgeVaultComponent, RecoverDeleteComponent, RecoverTwoFactorComponent, @@ -246,7 +241,6 @@ import { SharedModule } from "./shared.module"; UserLayoutComponent, VerifyEmailTokenComponent, VerifyRecoverDeleteComponent, - VerifyRecoverDeleteProviderComponent, HeaderModule, DangerZoneComponent, ], diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts index 55c22ec4cea..00c944e69bb 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-routing.module.ts @@ -4,7 +4,6 @@ import { RouterModule, Routes } from "@angular/router"; import { authGuard } from "@bitwarden/angular/auth/guards"; import { AnonLayoutWrapperComponent } from "@bitwarden/auth/angular"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; -import { ProvidersComponent } from "@bitwarden/web-vault/app/admin-console/providers/providers.component"; import { FrontendLayoutComponent } from "@bitwarden/web-vault/app/layouts/frontend-layout.component"; import { UserLayoutComponent } from "@bitwarden/web-vault/app/layouts/user-layout.component"; @@ -22,6 +21,7 @@ import { AcceptProviderComponent } from "./manage/accept-provider.component"; import { EventsComponent } from "./manage/events.component"; import { MembersComponent } from "./manage/members.component"; import { ProvidersLayoutComponent } from "./providers-layout.component"; +import { ProvidersComponent } from "./providers.component"; import { AccountComponent } from "./settings/account.component"; import { SetupProviderComponent } from "./setup/setup-provider.component"; import { SetupComponent } from "./setup/setup.component"; diff --git a/apps/web/src/app/admin-console/providers/providers.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.component.html similarity index 100% rename from apps/web/src/app/admin-console/providers/providers.component.html rename to bitwarden_license/bit-web/src/app/admin-console/providers/providers.component.html diff --git a/apps/web/src/app/admin-console/providers/providers.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.component.ts similarity index 100% rename from apps/web/src/app/admin-console/providers/providers.component.ts rename to bitwarden_license/bit-web/src/app/admin-console/providers/providers.component.ts diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index b6c7125c48c..80108e66eda 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -32,10 +32,12 @@ import { MembersComponent } from "./manage/members.component"; import { UserAddEditComponent } from "./manage/user-add-edit.component"; import { ProvidersLayoutComponent } from "./providers-layout.component"; import { ProvidersRoutingModule } from "./providers-routing.module"; +import { ProvidersComponent } from "./providers.component"; import { WebProviderService } from "./services/web-provider.service"; import { AccountComponent } from "./settings/account.component"; import { SetupProviderComponent } from "./setup/setup-provider.component"; import { SetupComponent } from "./setup/setup.component"; +import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-provider.component"; @NgModule({ imports: [ @@ -73,6 +75,8 @@ import { SetupComponent } from "./setup/setup.component"; ProviderBillingHistoryComponent, ProviderSubscriptionComponent, ProviderSubscriptionStatusComponent, + ProvidersComponent, + VerifyRecoverDeleteProviderComponent, ], providers: [WebProviderService], }) diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.html new file mode 100644 index 00000000000..116e1660d7a --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.html @@ -0,0 +1,15 @@ +

{{ "deleteProvider" | i18n }}

+{{ "deleteProviderWarning" | i18n }} +

+ {{ name }} +

+

{{ "deleteProviderRecoverConfirmDesc" | i18n }}

+
+
+ + + {{ "cancel" | i18n }} + +
diff --git a/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts similarity index 63% rename from apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts rename to bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts index dc6fa099610..a4461b3e11a 100644 --- a/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts @@ -5,8 +5,6 @@ import { firstValueFrom } from "rxjs"; import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { ProviderVerifyRecoverDeleteRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-verify-recover-delete.request"; 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 { ToastService } from "@bitwarden/components"; @Component({ @@ -16,7 +14,6 @@ import { ToastService } from "@bitwarden/components"; // eslint-disable-next-line rxjs-angular/prefer-takeuntil export class VerifyRecoverDeleteProviderComponent implements OnInit { name: string; - formPromise: Promise; private providerId: string; private token: string; @@ -24,10 +21,8 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit { constructor( private router: Router, private providerApiService: ProviderApiServiceAbstraction, - private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private route: ActivatedRoute, - private logService: LogService, private toastService: ToastService, ) {} @@ -42,22 +37,14 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit { } } - async submit() { - try { - const request = new ProviderVerifyRecoverDeleteRequest(this.token); - this.formPromise = this.providerApiService.providerRecoverDeleteToken( - this.providerId, - request, - ); - await this.formPromise; - this.toastService.showToast({ - variant: "success", - title: this.i18nService.t("providerDeleted"), - message: this.i18nService.t("providerDeletedDesc"), - }); - await this.router.navigate(["/"]); - } catch (e) { - this.logService.error(e); - } - } + submit = async () => { + const request = new ProviderVerifyRecoverDeleteRequest(this.token); + await this.providerApiService.providerRecoverDeleteToken(this.providerId, request); + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("providerDeleted"), + message: this.i18nService.t("providerDeletedDesc"), + }); + await this.router.navigate(["/"]); + }; } diff --git a/bitwarden_license/bit-web/src/app/app-routing.module.ts b/bitwarden_license/bit-web/src/app/app-routing.module.ts index f3f3c158708..6aed12511c1 100644 --- a/bitwarden_license/bit-web/src/app/app-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/app-routing.module.ts @@ -1,9 +1,13 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; +import { unauthGuardFn } from "@bitwarden/angular/auth/guards"; +import { AnonLayoutWrapperComponent } from "@bitwarden/auth/angular"; import { deepLinkGuard } from "@bitwarden/web-vault/app/auth/guards/deep-link.guard"; +import { RouteDataProperties } from "@bitwarden/web-vault/app/core"; import { ProvidersModule } from "./admin-console/providers/providers.module"; +import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component"; const routes: Routes = [ { @@ -17,6 +21,18 @@ const routes: Routes = [ loadChildren: async () => (await import("./secrets-manager/secrets-manager.module")).SecretsManagerModule, }, + { + path: "verify-recover-delete-provider", + component: AnonLayoutWrapperComponent, + canActivate: [unauthGuardFn()], + children: [ + { + path: "", + component: VerifyRecoverDeleteProviderComponent, + data: { titleId: "deleteAccount" } satisfies RouteDataProperties, + }, + ], + }, ]; @NgModule({ From 53f13f4ea549fca217b8716ca00b691e94320071 Mon Sep 17 00:00:00 2001 From: Jared McCannon Date: Mon, 28 Oct 2024 11:00:39 -0500 Subject: [PATCH 109/126] [PM-10314] Update Warnings for Verifying Domains and Single Org Policy Auto Enable (#11688) * Updated description and warning for single org policy. * Added check for verified domains in disabling single org. --- .../policies/policy-edit.component.html | 2 +- .../policies/policy-edit.component.ts | 3 ++ .../policies/single-org.component.html | 9 +++- .../policies/single-org.component.ts | 31 ++++++++++++- apps/web/src/locales/en/messages.json | 12 ++++++ .../domain-verification.component.ts | 43 +++++++++++++++++-- .../models/response/policy.response.ts | 2 + 7 files changed, 93 insertions(+), 9 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html index 20f62c1be0b..f656d488e06 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html @@ -15,7 +15,7 @@ - + + +