From 50b3e40a0573f18064f666812434e122c5d66c40 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Mon, 7 Aug 2023 16:06:25 -0500 Subject: [PATCH 001/135] [PM-2147] [BEEEP] Open login form used to unlock extension in a separate window instead of a tab (#5384) * [PM-1796] The autofill keyboard shortcut does not prompt a user to unlock a locked extension within an incongito browsing session * [PM-1796] Implementing fixes for how we handle focus redirection when logging a user in and attempting to autofill within the Firefox Workspaces addon * [PM-1796] Removing the `openerTab` value from the createNewTab method within brwoserApi.ts * [PM-1796] Removing async declaration from createNewTab * [PM-1796] Removing unnecessary param from the call to openBitwardenExtrensionTab * [PM-2147] [BEEEP] Open login form used to unlock extension in a separate window instead of a tab * [PM-2147] [BEEEP] Open login form used to unlock extension in a separate window instead of a tab * [PM-2147] [BEEEP] Modifying the position where the window opens and starting cleanup of comments within implementation * [PM-2147] [BEEEP] Cleaning up comments within implementation * [PM-2147] [BEEEP] Removing unnecessary method * [PM-2147] [BEEEP] Removing package-lock changes * [PM-2147] [BEEEP] Cleaning up implementation * [PM-2147] [BEEEP] Reverting addition to the whitelist-capital-letters filter and updating named file * [PM-2147] [BEEEP] Reverting addition to the whitelist-capital-letters filter and updating named file * [PM-2147] [BEEEP] Adjusting implementation of notifications bar to trigger presentation on lock only when not adding a new vault item * [PM-2147] [BEEEP] Adjusting implementation of how we open a login prompt window to ensure we are showing the address bar to the user * [PM-2147] [BEEEP] Modifying the method closeBitwardenLoginPromptWindow to not check for a popup type window * [PM-2147] [BEEEP] Fixing bug where notification bar does not close when unlocking vault * [PM-2147] [BEEEP] Adjusting placement of method BrowserApi.getWindow to have it present closer to getTab * [PM-2147] [BEEEP] Implementing a sepearate service BrowserPopoutService that will maintain the most recently created popouts and selectively remove those when re-opening the login prompt * [PM-2147] [BEEEP] Modifying position of BrowserPopoutWindowService * [PM-2147] [BEEEP] Modifying position of BrowserPopoutWindowService * [PM-2147] [BEEEP] Modifying how we handle identifying a single use popout --- apps/browser/src/_locales/en/messages.json | 6 ++ .../background/context-menus.background.ts | 1 + .../background/notification.background.ts | 73 +++++++++++++++++-- .../src/autofill/notification/bar.html | 9 +++ apps/browser/src/autofill/notification/bar.ts | 22 ++++++ .../browser/src/background/main.background.ts | 6 +- .../models/add-unlock-vault-queue-message.ts | 6 ++ .../lockedVaultPendingNotificationsItem.ts | 5 +- .../models/notificationQueueMessageType.ts | 1 + .../src/background/runtime.background.ts | 9 ++- .../src/platform/browser/browser-api.ts | 24 +++++- .../browser-popout-window.service.ts | 6 ++ .../popup/browser-popout-window.service.ts | 64 ++++++++++++++++ 13 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 apps/browser/src/background/models/add-unlock-vault-queue-message.ts create mode 100644 apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts create mode 100644 apps/browser/src/platform/popup/browser-popout-window.service.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index b5d5afb27c4..63dd227e52b 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, diff --git a/apps/browser/src/autofill/background/context-menus.background.ts b/apps/browser/src/autofill/background/context-menus.background.ts index 681f86cdf67..bc26353cbd9 100644 --- a/apps/browser/src/autofill/background/context-menus.background.ts +++ b/apps/browser/src/autofill/background/context-menus.background.ts @@ -30,6 +30,7 @@ export default class ContextMenusBackground { msg.data.commandToRetry.msg.data, msg.data.commandToRetry.sender.tab ); + await BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar"); } } ); diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 07b565e2608..1cb006fa3a2 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -12,6 +12,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import AddUnlockVaultQueueMessage from "../../background/models/add-unlock-vault-queue-message"; import AddChangePasswordQueueMessage from "../../background/models/addChangePasswordQueueMessage"; import AddLoginQueueMessage from "../../background/models/addLoginQueueMessage"; import AddLoginRuntimeMessage from "../../background/models/addLoginRuntimeMessage"; @@ -23,7 +24,11 @@ import { BrowserStateService } from "../../platform/services/abstractions/browse import { AutofillService } from "../services/abstractions/autofill.service"; export default class NotificationBackground { - private notificationQueue: (AddLoginQueueMessage | AddChangePasswordQueueMessage)[] = []; + private notificationQueue: ( + | AddLoginQueueMessage + | AddChangePasswordQueueMessage + | AddUnlockVaultQueueMessage + )[] = []; constructor( private autofillService: AutofillService, @@ -53,10 +58,7 @@ export default class NotificationBackground { async processMessage(msg: any, sender: chrome.runtime.MessageSender) { switch (msg.command) { case "unlockCompleted": - if (msg.data.target !== "notification.background") { - return; - } - await this.processMessage(msg.data.commandToRetry.msg, msg.data.commandToRetry.sender); + await this.handleUnlockCompleted(msg.data, sender); break; case "bgGetDataForTab": await this.getDataForTab(sender.tab, msg.responseCommand); @@ -82,7 +84,9 @@ export default class NotificationBackground { if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) { const retryMessage: LockedVaultPendingNotificationsItem = { commandToRetry: { - msg: msg, + msg: { + command: msg, + }, sender: sender, }, target: "notification.background", @@ -114,6 +118,9 @@ export default class NotificationBackground { break; } break; + case "promptForLogin": + await this.unlockVault(sender.tab); + break; default: break; } @@ -181,6 +188,14 @@ export default class NotificationBackground { webVaultURL: await this.environmentService.getWebVaultUrl(), }, }); + } else if (this.notificationQueue[i].type === NotificationQueueMessageType.UnlockVault) { + BrowserApi.tabSendMessageData(tab, "openNotificationBar", { + type: "unlock", + typeData: { + isVaultLocked: this.notificationQueue[i].wasVaultLocked, + theme: await this.getCurrentTheme(), + }, + }); } break; } @@ -305,6 +320,20 @@ export default class NotificationBackground { } } + private async unlockVault(tab: chrome.tabs.Tab) { + const currentAuthStatus = await this.authService.getAuthStatus(); + if (currentAuthStatus !== AuthenticationStatus.Locked || this.notificationQueue.length) { + return; + } + + const loginDomain = Utils.getDomain(tab.url); + if (!loginDomain) { + return; + } + + this.pushUnlockVaultToQueue(loginDomain, tab); + } + private async pushChangePasswordToQueue( cipherId: string, loginDomain: string, @@ -327,6 +356,20 @@ export default class NotificationBackground { await this.checkNotificationQueue(tab); } + private async pushUnlockVaultToQueue(loginDomain: string, tab: chrome.tabs.Tab) { + this.removeTabFromNotificationQueue(tab); + const message: AddUnlockVaultQueueMessage = { + type: NotificationQueueMessageType.UnlockVault, + domain: loginDomain, + tabId: tab.id, + expires: new Date(new Date().getTime() + 0.5 * 60000), // 30 seconds + wasVaultLocked: true, + }; + this.notificationQueue.push(message); + await this.checkNotificationQueue(tab); + this.removeTabFromNotificationQueue(tab); + } + private async saveOrUpdateCredentials(tab: chrome.tabs.Tab, edit: boolean, folderId?: string) { for (let i = this.notificationQueue.length - 1; i >= 0; i--) { const queueMessage = this.notificationQueue[i]; @@ -463,4 +506,22 @@ export default class NotificationBackground { this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership) ); } + + private async handleUnlockCompleted( + messageData: LockedVaultPendingNotificationsItem, + sender: chrome.runtime.MessageSender + ): Promise { + if (messageData.commandToRetry.msg.command === "autofill_login") { + await BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar"); + } + + if (messageData.target !== "notification.background") { + return; + } + + await this.processMessage( + messageData.commandToRetry.msg.command, + messageData.commandToRetry.sender + ); + } } diff --git a/apps/browser/src/autofill/notification/bar.html b/apps/browser/src/autofill/notification/bar.html index deec7fd512c..a6be58de70a 100644 --- a/apps/browser/src/autofill/notification/bar.html +++ b/apps/browser/src/autofill/notification/bar.html @@ -51,4 +51,13 @@ + + diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 728ae4e1287..dcc4ce010f6 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -28,6 +28,8 @@ function load() { notificationEdit: chrome.i18n.getMessage("edit"), notificationChangeSave: chrome.i18n.getMessage("notificationChangeSave"), notificationChangeDesc: chrome.i18n.getMessage("notificationChangeDesc"), + notificationUnlock: chrome.i18n.getMessage("notificationUnlock"), + notificationUnlockDesc: chrome.i18n.getMessage("notificationUnlockDesc"), }; const logoLink = document.getElementById("logo-link") as HTMLAnchorElement; @@ -72,6 +74,13 @@ function load() { changeTemplate.content.getElementById("change-text").textContent = i18n.notificationChangeDesc; + const unlockTemplate = document.getElementById("template-unlock") as HTMLTemplateElement; + + const unlockButton = unlockTemplate.content.getElementById("unlock-vault"); + unlockButton.textContent = i18n.notificationUnlock; + + unlockTemplate.content.getElementById("unlock-text").textContent = i18n.notificationUnlockDesc; + // i18n for body content const closeButton = document.getElementById("close-button"); closeButton.title = i18n.close; @@ -80,6 +89,8 @@ function load() { handleTypeAdd(); } else if (getQueryVariable("type") === "change") { handleTypeChange(); + } else if (getQueryVariable("type") === "unlock") { + handleTypeUnlock(); } closeButton.addEventListener("click", (e) => { @@ -172,6 +183,17 @@ function handleTypeChange() { }); } +function handleTypeUnlock() { + setContent(document.getElementById("template-unlock") as HTMLTemplateElement); + + const unlockButton = document.getElementById("unlock-vault"); + unlockButton.addEventListener("click", (e) => { + sendPlatformMessage({ + command: "bgReopenPromptForLogin", + }); + }); +} + function setContent(template: HTMLTemplateElement) { const content = document.getElementById("content"); while (content.firstChild) { diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 4e310354699..59a1b444e09 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -114,6 +114,7 @@ import { Account } from "../models/account"; import { BrowserApi } from "../platform/browser/browser-api"; import { flagEnabled } from "../platform/flags"; import { UpdateBadge } from "../platform/listeners/update-badge"; +import BrowserPopoutWindowService from "../platform/popup/browser-popout-window.service"; import { BrowserStateService as StateServiceAbstraction } from "../platform/services/abstractions/browser-state.service"; import { BrowserCryptoService } from "../platform/services/browser-crypto.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; @@ -195,6 +196,7 @@ export default class MainBackground { cipherContextMenuHandler: CipherContextMenuHandler; configService: ConfigServiceAbstraction; configApiService: ConfigApiServiceAbstraction; + browserPopoutWindowService: BrowserPopoutWindowService; // Passed to the popup for Safari to workaround issues with theming, downloading, etc. backgroundWindow = window; @@ -512,6 +514,7 @@ export default class MainBackground { this.authService, this.environmentService ); + this.browserPopoutWindowService = new BrowserPopoutWindowService(); const systemUtilsServiceReloadCallback = () => { const forceWindowReload = @@ -543,7 +546,8 @@ export default class MainBackground { this.environmentService, this.messagingService, this.logService, - this.configService + this.configService, + this.browserPopoutWindowService ); this.nativeMessagingBackground = new NativeMessagingBackground( this.cryptoService, diff --git a/apps/browser/src/background/models/add-unlock-vault-queue-message.ts b/apps/browser/src/background/models/add-unlock-vault-queue-message.ts new file mode 100644 index 00000000000..9ddde271008 --- /dev/null +++ b/apps/browser/src/background/models/add-unlock-vault-queue-message.ts @@ -0,0 +1,6 @@ +import NotificationQueueMessage from "./notificationQueueMessage"; +import { NotificationQueueMessageType } from "./notificationQueueMessageType"; + +export default class AddUnlockVaultQueueMessage extends NotificationQueueMessage { + type: NotificationQueueMessageType.UnlockVault; +} diff --git a/apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts b/apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts index ec697b16994..53f8405cd50 100644 --- a/apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts +++ b/apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts @@ -1,6 +1,9 @@ export default class LockedVaultPendingNotificationsItem { commandToRetry: { - msg: any; + msg: { + command: string; + data?: any; + }; sender: chrome.runtime.MessageSender; }; target: string; diff --git a/apps/browser/src/background/models/notificationQueueMessageType.ts b/apps/browser/src/background/models/notificationQueueMessageType.ts index f5e4115c4f5..2ce1a1840d8 100644 --- a/apps/browser/src/background/models/notificationQueueMessageType.ts +++ b/apps/browser/src/background/models/notificationQueueMessageType.ts @@ -1,4 +1,5 @@ export enum NotificationQueueMessageType { AddLogin = 0, ChangePassword = 1, + UnlockVault = 2, } diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 81f7376a94d..2e9fc934882 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -8,6 +8,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { AutofillService } from "../autofill/services/abstractions/autofill.service"; import { BrowserApi } from "../platform/browser/browser-api"; +import { BrowserPopoutWindowService } from "../platform/popup/abstractions/browser-popout-window.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service"; @@ -30,7 +31,8 @@ export default class RuntimeBackground { private environmentService: BrowserEnvironmentService, private messagingService: MessagingService, private logService: LogService, - private configService: ConfigServiceAbstraction + private configService: ConfigServiceAbstraction, + private browserPopoutWindowService: BrowserPopoutWindowService ) { // onInstalled listener must be wired up before anything else, so we do it in the ctor chrome.runtime.onInstalled.addListener((details: any) => { @@ -66,7 +68,7 @@ export default class RuntimeBackground { if (this.lockedVaultPendingNotifications?.length > 0) { item = this.lockedVaultPendingNotifications.pop(); - BrowserApi.closeBitwardenExtensionTab(); + await this.browserPopoutWindowService.closeLoginPrompt(); } await this.main.refreshBadge(); @@ -105,7 +107,8 @@ export default class RuntimeBackground { await this.main.openPopup(); break; case "promptForLogin": - BrowserApi.openBitwardenExtensionTab("popup/index.html", true); + case "bgReopenPromptForLogin": + await this.browserPopoutWindowService.openLoginPrompt(sender.tab?.windowId); break; case "openAddEditCipher": { const addEditCipherUrl = diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 7c646e5c7e6..243971dbfc0 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -17,6 +17,24 @@ export class BrowserApi { return chrome.runtime.getManifest().manifest_version; } + static getWindow(windowId?: number): Promise | void { + if (!windowId) { + return; + } + + return new Promise((resolve) => + chrome.windows.get(windowId, { populate: true }, (window) => resolve(window)) + ); + } + + static async createWindow(options: chrome.windows.CreateData): Promise { + return new Promise((resolve) => + chrome.windows.create(options, (window) => { + resolve(window); + }) + ); + } + static async getTabFromCurrentWindowId(): Promise | null { return await BrowserApi.tabsQueryFirst({ active: true, @@ -105,6 +123,10 @@ export class BrowserApi { chrome.tabs.sendMessage(tabId, message, options, responseCallback); } + static async removeTab(tabId: number) { + await chrome.tabs.remove(tabId); + } + static async getPrivateModeWindows(): Promise { return (await browser.windows.getAll()).filter((win) => win.incognito); } @@ -165,7 +187,7 @@ export class BrowserApi { } const tabToClose = tabs[tabs.length - 1]; - chrome.tabs.remove(tabToClose.id); + BrowserApi.removeTab(tabToClose.id); } private static registeredMessageListeners: any[] = []; diff --git a/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts b/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts new file mode 100644 index 00000000000..ca22e369d80 --- /dev/null +++ b/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts @@ -0,0 +1,6 @@ +interface BrowserPopoutWindowService { + openLoginPrompt(senderWindowId: number): Promise; + closeLoginPrompt(): Promise; +} + +export { BrowserPopoutWindowService }; diff --git a/apps/browser/src/platform/popup/browser-popout-window.service.ts b/apps/browser/src/platform/popup/browser-popout-window.service.ts new file mode 100644 index 00000000000..bfec3e690ba --- /dev/null +++ b/apps/browser/src/platform/popup/browser-popout-window.service.ts @@ -0,0 +1,64 @@ +import { BrowserApi } from "../browser/browser-api"; + +import { BrowserPopoutWindowService as BrowserPopupWindowServiceInterface } from "./abstractions/browser-popout-window.service"; + +class BrowserPopoutWindowService implements BrowserPopupWindowServiceInterface { + private singleActionPopoutTabIds: Record = {}; + private defaultPopoutWindowOptions: chrome.windows.CreateData = { + type: "normal", + focused: true, + width: 500, + height: 800, + }; + + async openLoginPrompt(senderWindowId: number) { + await this.closeLoginPrompt(); + await this.openPopoutWindow( + senderWindowId, + "popup/index.html?uilocation=popout", + "loginPrompt" + ); + } + + async closeLoginPrompt() { + await this.closeSingleActionPopout("loginPrompt"); + } + + private async openPopoutWindow( + senderWindowId: number, + popupWindowURL: string, + singleActionPopoutKey: string + ) { + const senderWindow = senderWindowId && (await BrowserApi.getWindow(senderWindowId)); + const url = chrome.extension.getURL(popupWindowURL); + const offsetRight = 15; + const offsetTop = 90; + const popupWidth = this.defaultPopoutWindowOptions.width; + const windowOptions = senderWindow + ? { + ...this.defaultPopoutWindowOptions, + url, + left: senderWindow.left + senderWindow.width - popupWidth - offsetRight, + top: senderWindow.top + offsetTop, + } + : { ...this.defaultPopoutWindowOptions, url }; + + const popupWindow = await BrowserApi.createWindow(windowOptions); + + if (!singleActionPopoutKey) { + return; + } + this.singleActionPopoutTabIds[singleActionPopoutKey] = popupWindow?.tabs[0].id; + } + + private async closeSingleActionPopout(popoutKey: string) { + const tabId = this.singleActionPopoutTabIds[popoutKey]; + if (!tabId) { + return; + } + await BrowserApi.removeTab(tabId); + this.singleActionPopoutTabIds[popoutKey] = null; + } +} + +export default BrowserPopoutWindowService; From f43272f243d9f969d6957db7e3a91a9ce75e2e5b Mon Sep 17 00:00:00 2001 From: Will Martin Date: Tue, 8 Aug 2023 09:37:25 -0400 Subject: [PATCH 002/135] [PM-3172] create @bitwarden/auth lib (#5906) * scaffold new lib * update jest config * update tsconfig * add readme * update tailwind config * update package-lock * update tsconfigs * update jest displayName * update tsconfig.libs.json * fix alias glob * update package lock * add readme to whitelist-capital-letters * update CODEOWNERS * remove test utils * update eslint rules * alphabetize eslint and tsconfig * sort jest config --------- Co-authored-by: Oscar Hinton --- .eslintrc.json | 26 ++++++++++++++--------- .github/CODEOWNERS | 1 + .github/whitelist-capital-letters.txt | 1 + apps/browser/tsconfig.json | 1 + apps/desktop/tsconfig.json | 1 + apps/web/tsconfig.json | 7 ++++--- bitwarden_license/bit-web/tsconfig.json | 7 ++++--- jest.config.js | 3 ++- libs/auth/README.md | 3 +++ libs/auth/jest.config.js | 16 ++++++++++++++ libs/auth/package.json | 20 ++++++++++++++++++ libs/auth/src/index.ts | 0 libs/auth/test.setup.ts | 28 +++++++++++++++++++++++++ libs/auth/tsconfig.json | 5 +++++ libs/auth/tsconfig.spec.json | 4 ++++ libs/shared/tsconfig.libs.json | 7 ++++--- package-lock.json | 9 ++++++++ tailwind.config.js | 1 + tsconfig.eslint.json | 7 ++++--- tsconfig.json | 7 ++++--- 20 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 libs/auth/README.md create mode 100644 libs/auth/jest.config.js create mode 100644 libs/auth/package.json create mode 100644 libs/auth/src/index.ts create mode 100644 libs/auth/test.setup.ts create mode 100644 libs/auth/tsconfig.json create mode 100644 libs/auth/tsconfig.spec.json diff --git a/.eslintrc.json b/.eslintrc.json index 45a9d063a98..afa134a65ed 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -134,6 +134,18 @@ "tailwindcss/no-contradicting-classname": "error" } }, + { + "files": ["libs/angular/src/**/*.ts"], + "rules": { + "no-restricted-imports": ["error", { "patterns": ["@bitwarden/angular/*", "src/**/*"] }] + } + }, + { + "files": ["libs/auth/src/**/*.ts"], + "rules": { + "no-restricted-imports": ["error", { "patterns": ["@bitwarden/auth/*", "src/**/*"] }] + } + }, { "files": ["libs/common/src/**/*.ts"], "rules": { @@ -147,15 +159,9 @@ } }, { - "files": ["libs/angular/src/**/*.ts"], + "files": ["libs/exporter/src/**/*.ts"], "rules": { - "no-restricted-imports": ["error", { "patterns": ["@bitwarden/angular/*", "src/**/*"] }] - } - }, - { - "files": ["libs/node/src/**/*.ts"], - "rules": { - "no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }] + "no-restricted-imports": ["error", { "patterns": ["@bitwarden/exporter/*", "src/**/*"] }] } }, { @@ -165,9 +171,9 @@ } }, { - "files": ["libs/exporter/src/**/*.ts"], + "files": ["libs/node/src/**/*.ts"], "rules": { - "no-restricted-imports": ["error", { "patterns": ["@bitwarden/exporter/*", "src/**/*"] }] + "no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }] } } ] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 832a874607d..1aed9c1be8c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,6 +14,7 @@ apps/browser/src/auth @bitwarden/team-auth-dev apps/cli/src/auth @bitwarden/team-auth-dev apps/desktop/src/auth @bitwarden/team-auth-dev apps/web/src/app/auth @bitwarden/team-auth-dev +libs/auth @bitwarden/team-auth-dev # web connectors used for auth apps/web/src/connectors @bitwarden/team-auth-dev bitwarden_license/bit-web/src/app/auth @bitwarden/team-auth-dev diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index fd03af5f08a..17047f4333d 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -31,6 +31,7 @@ ./libs/common/src/services/vaultTimeout/vaultTimeoutSettings.service.ts ./libs/common/src/services/vaultTimeout/vaultTimeout.service.ts ./libs/common/src/services/anonymousHub.service.ts +./libs/auth/README.md ./README.md ./LICENSE_BITWARDEN.txt ./CONTRIBUTING.md diff --git a/apps/browser/tsconfig.json b/apps/browser/tsconfig.json index 6c5e4330b73..9220b89c424 100644 --- a/apps/browser/tsconfig.json +++ b/apps/browser/tsconfig.json @@ -11,6 +11,7 @@ "baseUrl": ".", "paths": { "@bitwarden/angular/*": ["../../libs/angular/src/*"], + "@bitwarden/auth": ["../../libs/auth/src"], "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/components": ["../../libs/components/src"], "@bitwarden/exporter/*": ["../../libs/exporter/src/*"] diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index bf55862559a..f34ba7cfae4 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -11,6 +11,7 @@ "baseUrl": ".", "paths": { "@bitwarden/angular/*": ["../../libs/angular/src/*"], + "@bitwarden/auth": ["../../libs/auth/src"], "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/components": ["../../libs/components/src"], "@bitwarden/exporter/*": ["../../libs/exporter/src/*"] diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index f5d6f72d96c..9048efd39b7 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -5,12 +5,13 @@ "module": "ES2020", "resolveJsonModule": true, "paths": { - "@bitwarden/web-vault/*": ["src/*"], - "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/angular/*": ["../../libs/angular/src/*"], + "@bitwarden/auth": ["../../libs/auth/src"], + "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/components": ["../../libs/components/src"], + "@bitwarden/exporter/*": ["../../libs/exporter/src/*"], "@bitwarden/importer": ["../../libs/importer/src"], - "@bitwarden/exporter/*": ["../../libs/exporter/src/*"] + "@bitwarden/web-vault/*": ["src/*"] } }, "angularCompilerOptions": { diff --git a/bitwarden_license/bit-web/tsconfig.json b/bitwarden_license/bit-web/tsconfig.json index 9f3a8224889..a712fae9025 100644 --- a/bitwarden_license/bit-web/tsconfig.json +++ b/bitwarden_license/bit-web/tsconfig.json @@ -2,11 +2,12 @@ "extends": "../../apps/web/tsconfig", "compilerOptions": { "paths": { - "@bitwarden/web-vault/*": ["../../apps/web/src/*"], - "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/angular/*": ["../../libs/angular/src/*"], + "@bitwarden/auth": ["../../libs/auth/src"], + "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/components": ["../../libs/components/src"], - "@bitwarden/exporter/*": ["../../libs/exporter/src/*"] + "@bitwarden/exporter/*": ["../../libs/exporter/src/*"], + "@bitwarden/web-vault/*": ["../../apps/web/src/*"] } }, "include": ["src/**/*.stories.ts"] diff --git a/jest.config.js b/jest.config.js index 8b54a826583..c4213fa143c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -21,10 +21,11 @@ module.exports = { "/bitwarden_license/bit-web/jest.config.js", "/libs/angular/jest.config.js", + "/libs/auth/jest.config.js", "/libs/common/jest.config.js", "/libs/components/jest.config.js", - "/libs/importer/jest.config.js", "/libs/exporter/jest.config.js", + "/libs/importer/jest.config.js", "/libs/node/jest.config.js", ], diff --git a/libs/auth/README.md b/libs/auth/README.md new file mode 100644 index 00000000000..59e6384fae4 --- /dev/null +++ b/libs/auth/README.md @@ -0,0 +1,3 @@ +# Auth + +This lib represents the public API of the Auth team at Bitwarden. Modules are imported using `@bitwarden/auth`. diff --git a/libs/auth/jest.config.js b/libs/auth/jest.config.js new file mode 100644 index 00000000000..3db83db07a4 --- /dev/null +++ b/libs/auth/jest.config.js @@ -0,0 +1,16 @@ +const { pathsToModuleNameMapper } = require("ts-jest"); + +const { compilerOptions } = require("../shared/tsconfig.libs"); + +const sharedConfig = require("../../libs/shared/jest.config.angular"); + +/** @type {import('jest').Config} */ +module.exports = { + ...sharedConfig, + displayName: "libs/auth tests", + preset: "jest-preset-angular", + setupFilesAfterEnv: ["/test.setup.ts"], + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { + prefix: "/", + }), +}; diff --git a/libs/auth/package.json b/libs/auth/package.json new file mode 100644 index 00000000000..52c1be63f81 --- /dev/null +++ b/libs/auth/package.json @@ -0,0 +1,20 @@ +{ + "name": "@bitwarden/auth", + "version": "0.0.0", + "description": "Common code used across Bitwarden JavaScript projects.", + "keywords": [ + "bitwarden" + ], + "author": "Bitwarden Inc.", + "homepage": "https://bitwarden.com", + "repository": { + "type": "git", + "url": "https://github.com/bitwarden/clients" + }, + "license": "GPL-3.0", + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && tsc", + "build:watch": "npm run clean && tsc -watch" + } +} diff --git a/libs/auth/src/index.ts b/libs/auth/src/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/auth/test.setup.ts b/libs/auth/test.setup.ts new file mode 100644 index 00000000000..6be6e7b8dd1 --- /dev/null +++ b/libs/auth/test.setup.ts @@ -0,0 +1,28 @@ +import { webcrypto } from "crypto"; +import "jest-preset-angular/setup-jest"; + +Object.defineProperty(window, "CSS", { value: null }); +Object.defineProperty(window, "getComputedStyle", { + value: () => { + return { + display: "none", + appearance: ["-webkit-appearance"], + }; + }, +}); + +Object.defineProperty(document, "doctype", { + value: "", +}); +Object.defineProperty(document.body.style, "transform", { + value: () => { + return { + enumerable: true, + configurable: true, + }; + }, +}); + +Object.defineProperty(window, "crypto", { + value: webcrypto, +}); diff --git a/libs/auth/tsconfig.json b/libs/auth/tsconfig.json new file mode 100644 index 00000000000..6004a56fb55 --- /dev/null +++ b/libs/auth/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../shared/tsconfig.libs", + "include": ["src", "spec"], + "exclude": ["node_modules", "dist"] +} diff --git a/libs/auth/tsconfig.spec.json b/libs/auth/tsconfig.spec.json new file mode 100644 index 00000000000..de184bd7608 --- /dev/null +++ b/libs/auth/tsconfig.spec.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "files": ["./test.setup.ts"] +} diff --git a/libs/shared/tsconfig.libs.json b/libs/shared/tsconfig.libs.json index 8eda1dc0d7c..28a1ccdfa4c 100644 --- a/libs/shared/tsconfig.libs.json +++ b/libs/shared/tsconfig.libs.json @@ -2,11 +2,12 @@ "extends": "./tsconfig", "compilerOptions": { "paths": { - "@bitwarden/common/*": ["../common/src/*"], "@bitwarden/angular/*": ["../angular/src/*"], - "@bitwarden/node/*": ["../node/src/*"], + "@bitwarden/auth": ["../auth/src/*"], + "@bitwarden/common/*": ["../common/src/*"], + "@bitwarden/exporter/*": ["../exporter/src/*"], "@bitwarden/importer": ["../importer/src"], - "@bitwarden/exporter/*": ["../exporter/src/*"] + "@bitwarden/node/*": ["../node/src/*"] } } } diff --git a/package-lock.json b/package-lock.json index bd51b35ce9f..df157a9cff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -250,6 +250,11 @@ "version": "0.0.0", "license": "GPL-3.0" }, + "libs/auth": { + "name": "@bitwarden/auth", + "version": "0.0.0", + "license": "GPL-3.0" + }, "libs/common": { "name": "@bitwarden/common", "version": "0.0.0", @@ -4392,6 +4397,10 @@ "resolved": "libs/angular", "link": true }, + "node_modules/@bitwarden/auth": { + "resolved": "libs/auth", + "link": true + }, "node_modules/@bitwarden/browser": { "resolved": "apps/browser", "link": true diff --git a/tailwind.config.js b/tailwind.config.js index 21012889938..4011729cf5a 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,6 +3,7 @@ const config = require("./libs/components/tailwind.config.base"); config.content = [ "./libs/components/src/**/*.{html,ts,mdx}", + "./libs/auth/src/**/*.{html,ts,mdx}", "./apps/web/src/**/*.{html,ts,mdx}", "./bitwarden_license/bit-web/src/**/*.{html,ts,mdx}", "./.storybook/preview.js", diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 3398efa9fdf..33037c385e2 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -15,11 +15,12 @@ "outDir": "dist", "baseUrl": ".", "paths": { - "@bitwarden/common/*": ["./libs/common/src/*"], "@bitwarden/angular/*": ["./libs/angular/src/*"], - "@bitwarden/node/*": ["./libs/node/src/*"], + "@bitwarden/auth": ["./libs/auth/src"], + "@bitwarden/common/*": ["./libs/common/src/*"], "@bitwarden/components": ["./libs/components/src"], - "@bitwarden/exporter/*": ["./libs/exporter/src/*"] + "@bitwarden/exporter/*": ["./libs/exporter/src/*"], + "@bitwarden/node/*": ["./libs/node/src/*"] }, "plugins": [ { diff --git a/tsconfig.json b/tsconfig.json index 649052c152b..41d92039705 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,12 +15,13 @@ "baseUrl": ".", "resolveJsonModule": true, "paths": { - "@bitwarden/common/*": ["./libs/common/src/*"], "@bitwarden/angular/*": ["./libs/angular/src/*"], - "@bitwarden/node/*": ["./libs/node/src/*"], + "@bitwarden/auth": ["./libs/auth/src"], + "@bitwarden/common/*": ["./libs/common/src/*"], "@bitwarden/components": ["./libs/components/src"], - "@bitwarden/importer": ["./libs/importer/src"], "@bitwarden/exporter/*": ["./libs/exporter/src/*"], + "@bitwarden/importer": ["./libs/importer/src"], + "@bitwarden/node/*": ["./libs/node/src/*"], "@bitwarden/web-vault/*": ["./apps/web/src/*"] }, "plugins": [ From eca060d7e6c18b6599bfce20b058a2cde8c514a7 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Tue, 8 Aug 2023 12:08:39 -0500 Subject: [PATCH 003/135] [SM-240] Add max length limit on SM strings (#5823) * Add max length limit on SM strings * remove HTML attributes * remove from project --- .../projects/dialog/project-dialog.component.html | 2 +- .../projects/dialog/project-dialog.component.ts | 2 +- .../secrets/dialog/secret-dialog.component.html | 2 +- .../secrets/dialog/secret-dialog.component.ts | 8 ++++---- .../dialog/service-account-dialog.component.html | 2 +- .../dialog/service-account-dialog.component.ts | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.html index 34082cd4d60..b56f7d45121 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.html @@ -7,7 +7,7 @@ {{ "projectName" | i18n }} - + diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts index 42ec8ef02f6..a6a3c958d09 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts @@ -27,7 +27,7 @@ export interface ProjectOperation { export class ProjectDialogComponent implements OnInit { protected formGroup = new FormGroup({ name: new FormControl("", { - validators: [Validators.required, BitValidators.trimValidator], + validators: [Validators.required, Validators.maxLength(500), BitValidators.trimValidator], updateOn: "submit", }), }); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html index d190a7f91be..62692511e23 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html @@ -41,7 +41,7 @@ {{ "projectName" | i18n }} - + diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts index 4bf9bd6f768..1c376739970 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts @@ -38,17 +38,17 @@ export interface SecretOperation { export class SecretDialogComponent implements OnInit { protected formGroup = new FormGroup({ name: new FormControl("", { - validators: [Validators.required, BitValidators.trimValidator], + validators: [Validators.required, Validators.maxLength(500), BitValidators.trimValidator], updateOn: "submit", }), - value: new FormControl("", [Validators.required]), + value: new FormControl("", [Validators.required, Validators.maxLength(3500)]), notes: new FormControl("", { - validators: [BitValidators.trimValidator], + validators: [Validators.maxLength(7000), BitValidators.trimValidator], updateOn: "submit", }), project: new FormControl("", [Validators.required]), newProjectName: new FormControl("", { - validators: [BitValidators.trimValidator], + validators: [Validators.maxLength(500), BitValidators.trimValidator], updateOn: "submit", }), }); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.html index dcd68aa6deb..5f7260025cf 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.html @@ -8,7 +8,7 @@
{{ "serviceAccountName" | i18n }} - +
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts index 68efb9e475d..1f42537f956 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts @@ -27,7 +27,7 @@ export class ServiceAccountDialogComponent { protected formGroup = new FormGroup( { name: new FormControl("", { - validators: [Validators.required, BitValidators.trimValidator], + validators: [Validators.required, Validators.maxLength(500), BitValidators.trimValidator], updateOn: "submit", }), }, From a7cce1a3ad89bfa1f12b08aa69358ae10a4e7b6b Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Tue, 8 Aug 2023 15:56:42 -0500 Subject: [PATCH 004/135] [PM-669] Right Click Autofill List Contains Incorrect Entries (#5711) --- apps/browser/src/autofill/background/tabs.background.ts | 2 ++ .../src/autofill/browser/cipher-context-menu-handler.ts | 6 ++++++ apps/browser/src/platform/background.ts | 2 ++ apps/browser/src/platform/listeners/index.ts | 6 ++++++ apps/browser/src/platform/listeners/update-badge.ts | 7 +++++++ 5 files changed, 23 insertions(+) diff --git a/apps/browser/src/autofill/background/tabs.background.ts b/apps/browser/src/autofill/background/tabs.background.ts index 0f724c84dd5..0655fd23b62 100644 --- a/apps/browser/src/autofill/background/tabs.background.ts +++ b/apps/browser/src/autofill/background/tabs.background.ts @@ -21,6 +21,8 @@ export default class TabsBackground { } this.focusedWindowId = windowId; + await this.main.refreshBadge(); + await this.main.refreshMenu(); this.main.messagingService.send("windowChanged"); }); diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index 2eccc03c12b..d4f72e56633 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -81,6 +81,12 @@ export class CipherContextMenuHandler { ); } + static async windowsOnFocusChangedListener(windowId: number, serviceCache: CachedServices) { + const cipherContextMenuHandler = await CipherContextMenuHandler.create(serviceCache); + const tab = await BrowserApi.getTabFromCurrentWindow(); + await cipherContextMenuHandler.update(tab?.url); + } + static async tabsOnActivatedListener( activeInfo: chrome.tabs.TabActiveInfo, serviceCache: CachedServices diff --git a/apps/browser/src/platform/background.ts b/apps/browser/src/platform/background.ts index 949991df9f0..f7913dade9f 100644 --- a/apps/browser/src/platform/background.ts +++ b/apps/browser/src/platform/background.ts @@ -8,6 +8,7 @@ import { onCommandListener, onInstallListener, runtimeMessageListener, + windowsOnFocusChangedListener, tabsOnActivatedListener, tabsOnReplacedListener, tabsOnUpdatedListener, @@ -18,6 +19,7 @@ if (BrowserApi.manifestVersion === 3) { chrome.runtime.onInstalled.addListener(onInstallListener); chrome.alarms.onAlarm.addListener(onAlarmListener); registerAlarms(); + chrome.windows.onFocusChanged.addListener(windowsOnFocusChangedListener); chrome.tabs.onActivated.addListener(tabsOnActivatedListener); chrome.tabs.onReplaced.addListener(tabsOnReplacedListener); chrome.tabs.onUpdated.addListener(tabsOnUpdatedListener); diff --git a/apps/browser/src/platform/listeners/index.ts b/apps/browser/src/platform/listeners/index.ts index cf4950023f3..60e304402aa 100644 --- a/apps/browser/src/platform/listeners/index.ts +++ b/apps/browser/src/platform/listeners/index.ts @@ -6,6 +6,11 @@ import { onCommandListener } from "./on-command-listener"; import { onInstallListener } from "./on-install-listener"; import { UpdateBadge } from "./update-badge"; +const windowsOnFocusChangedListener = combine([ + UpdateBadge.windowsOnFocusChangedListener, + CipherContextMenuHandler.windowsOnFocusChangedListener, +]); + const tabsOnActivatedListener = combine([ UpdateBadge.tabsOnActivatedListener, CipherContextMenuHandler.tabsOnActivatedListener, @@ -33,6 +38,7 @@ const runtimeMessageListener = combine< ]); export { + windowsOnFocusChangedListener, tabsOnActivatedListener, tabsOnReplacedListener, tabsOnUpdatedListener, diff --git a/apps/browser/src/platform/listeners/update-badge.ts b/apps/browser/src/platform/listeners/update-badge.ts index 4623ff30f05..89b620ad6fe 100644 --- a/apps/browser/src/platform/listeners/update-badge.ts +++ b/apps/browser/src/platform/listeners/update-badge.ts @@ -42,6 +42,13 @@ export class UpdateBadge { "deletedCipher", ]; + static async windowsOnFocusChangedListener( + windowId: number, + serviceCache: Record + ) { + await new UpdateBadge(self).run({ windowId, existingServices: serviceCache }); + } + static async tabsOnActivatedListener( activeInfo: chrome.tabs.TabActiveInfo, serviceCache: Record From 96c6b870cb58a166d8b4a4f81bdb9015aa79efdd Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Wed, 9 Aug 2023 14:10:01 -0400 Subject: [PATCH 005/135] PM-2593 Update Premium Urls to target cloudWebVaultUrl (#5861) * updated urls in premium web component and premium libs to use the cloudwebvaulturl * moved premium.component in web from base settings to a new settings folder inside vault --- .../src/popup/settings/premium.component.ts | 14 ++++++++++++-- .../src/vault/app/accounts/premium.component.ts | 14 ++++++++++++-- .../settings/subscription-routing.module.ts | 2 +- apps/web/src/app/shared/loose-components.module.ts | 2 +- .../{ => vault}/settings/premium.component.html | 2 +- .../app/{ => vault}/settings/premium.component.ts | 10 +++++++--- .../src/vault/components/premium.component.ts | 13 +++++++++---- 7 files changed, 43 insertions(+), 14 deletions(-) rename apps/web/src/app/{ => vault}/settings/premium.component.html (98%) rename apps/web/src/app/{ => vault}/settings/premium.component.ts (91%) diff --git a/apps/browser/src/popup/settings/premium.component.ts b/apps/browser/src/popup/settings/premium.component.ts index 5c179a12d72..459cd2a6cb2 100644 --- a/apps/browser/src/popup/settings/premium.component.ts +++ b/apps/browser/src/popup/settings/premium.component.ts @@ -4,6 +4,7 @@ import { Component } from "@angular/core"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -24,9 +25,18 @@ export class PremiumComponent extends BasePremiumComponent { logService: LogService, private location: Location, private currencyPipe: CurrencyPipe, - dialogService: DialogServiceAbstraction + dialogService: DialogServiceAbstraction, + environmentService: EnvironmentService ) { - super(i18nService, platformUtilsService, apiService, logService, stateService, dialogService); + super( + i18nService, + platformUtilsService, + apiService, + logService, + stateService, + dialogService, + environmentService + ); // Support old price string. Can be removed in future once all translations are properly updated. const thePrice = this.currencyPipe.transform(this.price, "$"); diff --git a/apps/desktop/src/vault/app/accounts/premium.component.ts b/apps/desktop/src/vault/app/accounts/premium.component.ts index 13116fd3ed4..fd275d6ed7d 100644 --- a/apps/desktop/src/vault/app/accounts/premium.component.ts +++ b/apps/desktop/src/vault/app/accounts/premium.component.ts @@ -3,6 +3,7 @@ import { Component } from "@angular/core"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -19,8 +20,17 @@ export class PremiumComponent extends BasePremiumComponent { apiService: ApiService, logService: LogService, stateService: StateService, - dialogService: DialogServiceAbstraction + dialogService: DialogServiceAbstraction, + environmentService: EnvironmentService ) { - super(i18nService, platformUtilsService, apiService, logService, stateService, dialogService); + super( + i18nService, + platformUtilsService, + apiService, + logService, + stateService, + dialogService, + environmentService + ); } } diff --git a/apps/web/src/app/billing/settings/subscription-routing.module.ts b/apps/web/src/app/billing/settings/subscription-routing.module.ts index 35b98b75d6a..43c7a6d7222 100644 --- a/apps/web/src/app/billing/settings/subscription-routing.module.ts +++ b/apps/web/src/app/billing/settings/subscription-routing.module.ts @@ -4,7 +4,7 @@ import { RouterModule, Routes } from "@angular/router"; import { BillingHistoryViewComponent } from "../../billing/settings/billing-history-view.component"; import { PaymentMethodComponent } from "../../billing/settings/payment-method.component"; import { UserSubscriptionComponent } from "../../billing/settings/user-subscription.component"; -import { PremiumComponent } from "../../settings/premium.component"; +import { PremiumComponent } from "../../vault/settings/premium.component"; import { SubscriptionComponent } from "./subscription.component"; diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 74a422d3b21..0acb456a41e 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -81,7 +81,6 @@ import { DeleteAccountComponent } from "../settings/delete-account.component"; import { DomainRulesComponent } from "../settings/domain-rules.component"; import { LowKdfComponent } from "../settings/low-kdf.component"; import { PreferencesComponent } from "../settings/preferences.component"; -import { PremiumComponent } from "../settings/premium.component"; import { ProfileComponent } from "../settings/profile.component"; import { PurgeVaultComponent } from "../settings/purge-vault.component"; import { SecurityKeysComponent } from "../settings/security-keys.component"; @@ -107,6 +106,7 @@ import { ShareComponent } from "../vault/individual-vault/share.component"; import { AddEditComponent as OrgAddEditComponent } from "../vault/org-vault/add-edit.component"; import { AttachmentsComponent as OrgAttachmentsComponent } from "../vault/org-vault/attachments.component"; import { CollectionsComponent as OrgCollectionsComponent } from "../vault/org-vault/collections.component"; +import { PremiumComponent } from "../vault/settings/premium.component"; import { EnvironmentSelectorModule } from "./../components/environment-selector/environment-selector.module"; import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component"; diff --git a/apps/web/src/app/settings/premium.component.html b/apps/web/src/app/vault/settings/premium.component.html similarity index 98% rename from apps/web/src/app/settings/premium.component.html rename to apps/web/src/app/vault/settings/premium.component.html index 3c7fadd86ff..b47bfc1be2b 100644 --- a/apps/web/src/app/settings/premium.component.html +++ b/apps/web/src/app/vault/settings/premium.component.html @@ -54,7 +54,7 @@

; @@ -39,9 +41,11 @@ export class PremiumComponent implements OnInit { private messagingService: MessagingService, private syncService: SyncService, private logService: LogService, - private stateService: StateService + private stateService: StateService, + private environmentService: EnvironmentService ) { this.selfHosted = platformUtilsService.isSelfHost(); + this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl(); } async ngOnInit() { diff --git a/libs/angular/src/vault/components/premium.component.ts b/libs/angular/src/vault/components/premium.component.ts index af66db7f4b6..b13cfa428d3 100644 --- a/libs/angular/src/vault/components/premium.component.ts +++ b/libs/angular/src/vault/components/premium.component.ts @@ -1,6 +1,7 @@ import { Directive, OnInit } from "@angular/core"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -13,6 +14,7 @@ export class PremiumComponent implements OnInit { isPremium = false; price = 10; refreshPromise: Promise; + cloudWebVaultUrl: string; constructor( protected i18nService: I18nService, @@ -20,8 +22,11 @@ export class PremiumComponent implements OnInit { protected apiService: ApiService, private logService: LogService, protected stateService: StateService, - protected dialogService: DialogServiceAbstraction - ) {} + protected dialogService: DialogServiceAbstraction, + private environmentService: EnvironmentService + ) { + this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl(); + } async ngOnInit() { this.isPremium = await this.stateService.getCanAccessPremium(); @@ -46,7 +51,7 @@ export class PremiumComponent implements OnInit { }); if (confirmed) { - this.platformUtilsService.launchUri("https://vault.bitwarden.com/#/?premium=purchase"); + this.platformUtilsService.launchUri(`${this.cloudWebVaultUrl}/#/?premium=purchase`); } } @@ -58,7 +63,7 @@ export class PremiumComponent implements OnInit { }); if (confirmed) { - this.platformUtilsService.launchUri("https://vault.bitwarden.com/#/?premium=manage"); + this.platformUtilsService.launchUri(`${this.cloudWebVaultUrl}/#/?premium=manage`); } } } From 3eab038a879f3855927bac10f223c795e751b025 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Wed, 9 Aug 2023 16:18:58 -0400 Subject: [PATCH 006/135] PM-2286 Master Password Reprompt on Attachments Option (#5844) * if master password reprompt is enabled the passwordRepromptModal will appear on attachments option in both individual and organization --- .../vault/individual-vault/vault.component.ts | 17 ++++++++++++----- .../src/app/vault/org-vault/vault.component.ts | 13 ++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 00e26b305de..70149cc0026 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -498,6 +498,11 @@ export class VaultComponent implements OnInit, OnDestroy { } async editCipherAttachments(cipher: CipherView) { + if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + this.go({ cipherId: null, itemId: null }); + return; + } + const canAccessPremium = await this.stateService.getCanAccessPremium(); if (cipher.organizationId == null && !canAccessPremium) { this.messagingService.send("premiumRequired"); @@ -539,6 +544,10 @@ export class VaultComponent implements OnInit, OnDestroy { } async shareCipher(cipher: CipherView) { + if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + this.go({ cipherId: null, itemId: null }); + return; + } const [modal] = await this.modalService.openViewRef( ShareComponent, this.shareModalRef, @@ -595,11 +604,9 @@ export class VaultComponent implements OnInit, OnDestroy { async editCipherId(id: string) { const cipher = await this.cipherService.get(id); - if (cipher != null && cipher.reprompt != 0) { - if (!(await this.passwordRepromptService.showPasswordPrompt())) { - this.go({ cipherId: null, itemId: null }); - return; - } + if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + this.go({ cipherId: null, itemId: null }); + return; } const [modal, childComponent] = await this.modalService.openViewRef( 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 63f92e64eae..8370b78cdc3 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -517,6 +517,11 @@ export class VaultComponent implements OnInit, OnDestroy { } async editCipherAttachments(cipher: CipherView) { + if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + this.go({ cipherId: null, itemId: null }); + return; + } + if (this.organization.maxStorageGb == null || this.organization.maxStorageGb === 0) { this.messagingService.send("upgradeOrganization", { organizationId: cipher.organizationId }); return; @@ -595,11 +600,9 @@ export class VaultComponent implements OnInit, OnDestroy { additionalComponentParameters?: (comp: AddEditComponent) => void ) { const cipher = await this.cipherService.get(cipherId); - if (cipher != null && cipher.reprompt != 0) { - if (!(await this.passwordRepromptService.showPasswordPrompt())) { - this.go({ cipherId: null, itemId: null }); - return; - } + if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + this.go({ cipherId: null, itemId: null }); + return; } const defaultComponentParameters = (comp: AddEditComponent) => { From 2187db2153ec7676dc7e1104ee10e0040782b807 Mon Sep 17 00:00:00 2001 From: Danielle Flinn <43477473+danielleflinn@users.noreply.github.com> Date: Thu, 10 Aug 2023 14:41:45 -0700 Subject: [PATCH 007/135] removed tw-uppercase from dialog title (#6008) --- libs/components/src/dialog/dialog/dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index 773b764b22e..b052cc23b66 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -6,7 +6,7 @@
-

+

+ + - - - + + + + diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts index 6309a5a8c6a..d8824cd7dd4 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts +++ b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts @@ -1,5 +1,6 @@ import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; +import { FormGroup } from "@angular/forms"; import { SimpleDialogType, @@ -29,8 +30,8 @@ const DEFAULT_COLOR: Record = { templateUrl: "./simple-configurable-dialog.component.html", }) export class SimpleConfigurableDialogComponent { - SimpleDialogType = SimpleDialogType; - SimpleDialogCloseType = SimpleDialogCloseType; + protected SimpleDialogType = SimpleDialogType; + protected SimpleDialogCloseType = SimpleDialogCloseType; get iconClasses() { return [ @@ -39,12 +40,13 @@ export class SimpleConfigurableDialogComponent { ]; } - title: string; - content: string; - acceptButtonText: string; - cancelButtonText: string; + protected title: string; + protected content: string; + protected acceptButtonText: string; + protected cancelButtonText: string; + protected formGroup = new FormGroup({}); - showCancelButton = this.simpleDialogOpts.cancelButtonText !== null; + protected showCancelButton = this.simpleDialogOpts.cancelButtonText !== null; constructor( public dialogRef: DialogRef, @@ -54,6 +56,14 @@ export class SimpleConfigurableDialogComponent { this.localizeText(); } + protected accept = async () => { + if (this.simpleDialogOpts.acceptAction) { + await this.simpleDialogOpts.acceptAction(); + } + + this.dialogRef.close(SimpleDialogCloseType.ACCEPT); + }; + private localizeText() { this.title = this.translate(this.simpleDialogOpts.title); this.content = this.translate(this.simpleDialogOpts.content); diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts index 998749da84f..0982db1ca7a 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts @@ -15,96 +15,17 @@ import { DialogModule } from "../dialog.module"; @Component({ template: ` -

Dialog Type Examples:

-
- - - - - - - - - -
- -

Custom Button Examples:

-
- - - - - -
- -

Custom Icon Example:

-
- -
- -

Additional Examples:

-
- +
+

{{ group.title }}

+
+ +
@@ -113,72 +34,93 @@ import { DialogModule } from "../dialog.module"; `, }) class StoryDialogComponent { - primaryLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - }; - - successLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("successTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.SUCCESS, - }; - - infoLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("infoTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.INFO, - }; - - warningLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("warningTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.WARNING, - }; - - dangerLocalizedSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("dangerTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.DANGER, - }; - - primarySingleBtnSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - acceptButtonText: "Ok", - cancelButtonText: null, - }; - - primaryCustomBtnsSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - acceptButtonText: this.i18nService.t("accept"), - cancelButtonText: this.i18nService.t("decline"), - }; - - primaryAcceptBtnOverrideSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - acceptButtonText: "Ok", - }; - - primaryCustomIconSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - icon: "bwi-family", - }; - - primaryDisableCloseSimpleDialogOpts: SimpleDialogOptions = { - title: this.i18nService.t("primaryTypeSimpleDialog"), - content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, - disableClose: true, - }; + protected dialogs: { title: string; dialogs: SimpleDialogOptions[] }[] = [ + { + title: "Regular", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + }, + { + title: this.i18nService.t("successTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.SUCCESS, + }, + { + title: this.i18nService.t("infoTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.INFO, + }, + { + title: this.i18nService.t("warningTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.WARNING, + }, + { + title: this.i18nService.t("dangerTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.DANGER, + }, + ], + }, + { + title: "Custom", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + acceptButtonText: "Ok", + cancelButtonText: null, + }, + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + acceptButtonText: this.i18nService.t("accept"), + cancelButtonText: this.i18nService.t("decline"), + }, + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + acceptButtonText: "Ok", + }, + ], + }, + { + title: "Icon", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + icon: "bwi-family", + }, + ], + }, + { + title: "Additional", + dialogs: [ + { + title: this.i18nService.t("primaryTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + type: SimpleDialogType.PRIMARY, + disableClose: true, + }, + { + title: this.i18nService.t("asyncTypeSimpleDialog"), + content: this.i18nService.t("dialogContent"), + acceptAction: () => { + return new Promise((resolve) => setTimeout(resolve, 10000)); + }, + type: SimpleDialogType.PRIMARY, + }, + ], + }, + ]; showCallout = false; calloutType = "info"; @@ -216,6 +158,7 @@ export default { infoTypeSimpleDialog: "Info Type Simple Dialog", warningTypeSimpleDialog: "Warning Type Simple Dialog", dangerTypeSimpleDialog: "Danger Type Simple Dialog", + asyncTypeSimpleDialog: "Async", dialogContent: "Dialog content goes here", yes: "Yes", no: "No", From 3880aeed81944671e931a40b03e78f1fa689f9cf Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:04:44 -0400 Subject: [PATCH 009/135] Bump Firefox Min Version (#6025) * Bump Firefox Min Version * Remove Version Check for Biometrics Support --- apps/browser/src/manifest.json | 2 +- apps/browser/src/manifest.v3.json | 2 +- .../src/platform/services/browser-platform-utils.service.ts | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index a23032ae6a2..905c9abfbe9 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -105,7 +105,7 @@ "applications": { "gecko": { "id": "{446900e4-71c2-419f-a6a7-df9c091e268b}", - "strict_min_version": "42.0" + "strict_min_version": "91.0" } }, "sidebar_action": { diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 9a742e64e48..145b9516b89 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -113,7 +113,7 @@ "applications": { "gecko": { "id": "{446900e4-71c2-419f-a6a7-df9c091e268b}", - "strict_min_version": "42.0" + "strict_min_version": "91.0" } }, "sidebar_action": { diff --git a/apps/browser/src/platform/services/browser-platform-utils.service.ts b/apps/browser/src/platform/services/browser-platform-utils.service.ts index 79c52d615fb..018b1c623dc 100644 --- a/apps/browser/src/platform/services/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/browser-platform-utils.service.ts @@ -314,10 +314,6 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService return false; } - if (this.isFirefox()) { - return parseInt((await browser.runtime.getBrowserInfo()).version.split(".")[0], 10) >= 87; - } - return true; } From d16e588d61d3d20298d12e2a6b02a8b708bfb4e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:44:47 +0000 Subject: [PATCH 010/135] Autosync the updated translations (#6012) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 14 ++++++-------- apps/desktop/src/locales/ar/messages.json | 16 +++++++--------- apps/desktop/src/locales/az/messages.json | 14 ++++++-------- apps/desktop/src/locales/be/messages.json | 14 ++++++-------- apps/desktop/src/locales/bg/messages.json | 14 ++++++-------- apps/desktop/src/locales/bn/messages.json | 14 ++++++-------- apps/desktop/src/locales/bs/messages.json | 14 ++++++-------- apps/desktop/src/locales/ca/messages.json | 14 ++++++-------- apps/desktop/src/locales/cs/messages.json | 14 ++++++-------- apps/desktop/src/locales/cy/messages.json | 14 ++++++-------- apps/desktop/src/locales/da/messages.json | 14 ++++++-------- apps/desktop/src/locales/de/messages.json | 20 +++++++++----------- apps/desktop/src/locales/el/messages.json | 14 ++++++-------- apps/desktop/src/locales/en_GB/messages.json | 14 ++++++-------- apps/desktop/src/locales/en_IN/messages.json | 14 ++++++-------- apps/desktop/src/locales/eo/messages.json | 14 ++++++-------- apps/desktop/src/locales/es/messages.json | 14 ++++++-------- apps/desktop/src/locales/et/messages.json | 14 ++++++-------- apps/desktop/src/locales/eu/messages.json | 14 ++++++-------- apps/desktop/src/locales/fa/messages.json | 14 ++++++-------- apps/desktop/src/locales/fi/messages.json | 14 ++++++-------- apps/desktop/src/locales/fil/messages.json | 14 ++++++-------- apps/desktop/src/locales/fr/messages.json | 14 ++++++-------- apps/desktop/src/locales/gl/messages.json | 14 ++++++-------- apps/desktop/src/locales/he/messages.json | 14 ++++++-------- apps/desktop/src/locales/hi/messages.json | 14 ++++++-------- apps/desktop/src/locales/hr/messages.json | 14 ++++++-------- apps/desktop/src/locales/hu/messages.json | 14 ++++++-------- apps/desktop/src/locales/id/messages.json | 14 ++++++-------- apps/desktop/src/locales/it/messages.json | 14 ++++++-------- apps/desktop/src/locales/ja/messages.json | 14 ++++++-------- apps/desktop/src/locales/ka/messages.json | 14 ++++++-------- apps/desktop/src/locales/km/messages.json | 14 ++++++-------- apps/desktop/src/locales/kn/messages.json | 14 ++++++-------- apps/desktop/src/locales/ko/messages.json | 14 ++++++-------- apps/desktop/src/locales/lv/messages.json | 14 ++++++-------- apps/desktop/src/locales/me/messages.json | 14 ++++++-------- apps/desktop/src/locales/ml/messages.json | 14 ++++++-------- apps/desktop/src/locales/mr/messages.json | 14 ++++++-------- apps/desktop/src/locales/my/messages.json | 14 ++++++-------- apps/desktop/src/locales/nb/messages.json | 14 ++++++-------- apps/desktop/src/locales/ne/messages.json | 14 ++++++-------- apps/desktop/src/locales/nl/messages.json | 14 ++++++-------- apps/desktop/src/locales/nn/messages.json | 14 ++++++-------- apps/desktop/src/locales/or/messages.json | 14 ++++++-------- apps/desktop/src/locales/pl/messages.json | 14 ++++++-------- apps/desktop/src/locales/pt_BR/messages.json | 14 ++++++-------- apps/desktop/src/locales/pt_PT/messages.json | 14 ++++++-------- apps/desktop/src/locales/ro/messages.json | 14 ++++++-------- apps/desktop/src/locales/ru/messages.json | 14 ++++++-------- apps/desktop/src/locales/si/messages.json | 14 ++++++-------- apps/desktop/src/locales/sk/messages.json | 14 ++++++-------- apps/desktop/src/locales/sl/messages.json | 14 ++++++-------- apps/desktop/src/locales/sr/messages.json | 14 ++++++-------- apps/desktop/src/locales/sv/messages.json | 14 ++++++-------- apps/desktop/src/locales/te/messages.json | 14 ++++++-------- apps/desktop/src/locales/th/messages.json | 14 ++++++-------- apps/desktop/src/locales/tr/messages.json | 14 ++++++-------- apps/desktop/src/locales/uk/messages.json | 14 ++++++-------- apps/desktop/src/locales/vi/messages.json | 14 ++++++-------- apps/desktop/src/locales/zh_CN/messages.json | 14 ++++++-------- apps/desktop/src/locales/zh_TW/messages.json | 14 ++++++-------- 62 files changed, 376 insertions(+), 500 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 59d7ef9c25d..5913597ee88 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aanbevole bywerking van instellings" }, - "region": { - "message": "Streek" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "VS", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Selghehuisves" diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 9012f6ee1ff..a65781e73d4 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -2252,21 +2252,19 @@ "windowsBiometricUpdateWarningTitle": { "message": "تحديث الإعدادات الموصى بها" }, - "region": { - "message": "المنطقة" + "loggingInOn": { + "message": "جارٍ تسجيل الدخول" }, - "eu": { - "message": "الاتحاد الأوروبي", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "الولايات المتحدة", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "استضافة ذاتية" }, "accessDenied": { - "message": "Access denied. You do not have permission to view this page." + "message": "غير مسموح بالدخول. ليس لديك الصلاحية لعرض هذه الصفحة." } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 965db4209db..3d1608a5490 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Tövsiyə edilən Tənzimləmələr Güncəlləməsi" }, - "region": { - "message": "Bölgə" + "loggingInOn": { + "message": "Giriş edilir" }, - "eu": { - "message": "AB", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ABŞ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Öz-özünə sahiblik edən" diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 1075fbe6da0..c8f182caeb0 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Рэкамендаваныя налады абнаўлення" }, - "region": { - "message": "Рэгіён" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "ЕС", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ЗША", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Уласнае размяшчэнне" diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 95231adb19a..27a7a85fc24 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Препоръчителна промяна на настройките" }, - "region": { - "message": "Регион" + "loggingInOn": { + "message": "Вписване в" }, - "eu": { - "message": "ЕС", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "САЩ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Собствен хостинг" diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 93d0c273940..1c33383916c 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index eabfc7f8067..61ea872f9aa 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 5429a271a2a..e098741ff5d 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Actualització de configuració recomanada" }, - "region": { - "message": "Regió" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "EUA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Autoallotjat" diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index d09e9809aad..8cef76fbe57 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aktualizace doporučených nastavení" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Přihlašování na" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Vlastní hosting" diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index fda5cea1451..9c182c1aeeb 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Anbefalet indstillingsopdatering" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logger ind på" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Selv-hostet" diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index fb470cc11ed..bf9ce7fe4a8 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -491,7 +491,7 @@ "message": "Ordner gelöscht" }, "loginOrCreateNewAccount": { - "message": "Einloggen oder einen neuen Account erstellen, um auf den Tresor zuzugreifen." + "message": "Melde dich an oder erstelle ein neues Konto, um auf deinen Tresor zuzugreifen." }, "createAccount": { "message": "Konto erstellen" @@ -615,7 +615,7 @@ } }, "rememberMe": { - "message": "Eingeloggt bleiben" + "message": "Angemeldet bleiben" }, "sendVerificationCodeEmailAgain": { "message": "E-Mail mit Bestätigungscode erneut versenden" @@ -824,7 +824,7 @@ "message": "Entsperren" }, "loggedInAsOn": { - "message": "Eingeloggt als $EMAIL$ auf $HOSTNAME$.", + "message": "Angemeldet als $EMAIL$ auf $HOSTNAME$.", "placeholders": { "email": { "content": "$1", @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Empfohlene Aktualisierung der Einstellungen" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Anmelden bei" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Selbst gehostet" diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index d6c8d1e50c3..43f20a5f21d 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Ενημέρωση Προτεινόμενων Ρυθμίσεων" }, - "region": { - "message": "Περιοχή" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "ΕΕ", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ΗΠΑ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 6a679b10417..428d6910662 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 637e752218d..d40c23fda48 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 1ece91c1e15..e72cd6515ea 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index c86bde31e87..9e31f09379a 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Actualización de ajustes recomendados" }, - "region": { - "message": "Región" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "Unión Europea", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "EE.UU.", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Autoalojado" diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index c2757bab62c..2b74589df25 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index b308429cec1..b2f553bbf35 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 65af270448b..655d016c0f1 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "به‌روز رسانی تنظیمات توصیه شده" }, - "region": { - "message": "منطقه" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "اروپا", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "امریکا", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "خود میزبان" diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 6f28c0c9afd..ac4e0ad6c68 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Suositeltava asetusmuutos" }, - "region": { - "message": "Alue" + "loggingInOn": { + "message": "Kirjaudutaan sijaintiin" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Itse ylläpidetty" diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 590d96aeb8d..99719bd9c78 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 15c54705fa6..15c77a1a49b 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Une mise à jour des paramètres est recommandée" }, - "region": { - "message": "Région" + "loggingInOn": { + "message": "Connexion sur" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Auto-hébergé" diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 46f39122f14..e804caddc6f 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 1b1a8a1b8e5..c8e4e16e954 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 23b4bebbabb..042af715e53 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index b1897f6e4b8..74745499079 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Ajánlott beállítások frissítése" }, - "region": { - "message": "Régió" + "loggingInOn": { + "message": "Bejelentkezés:" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Saját kiszolgáló" diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index c4a8b77566c..21bbdd12c36 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index fbbcb2f08c1..3e977e9f200 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aggiornamento delle impostazioni consigliato" }, - "region": { - "message": "Regione" + "loggingInOn": { + "message": "Accedendo su" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 6e89627a5e9..155af2e5259 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "設定の更新を推奨" }, - "region": { - "message": "リージョン" + "loggingInOn": { + "message": "ログイン先" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "米国", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "自己ホスト型" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 0d1ea3265af..24833fa4383 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 2699d1797d9..87dad22d31e 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index fe5d4dec7ff..961e3cf593c 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Ieteicamie iestatījumu atjauninājumi" }, - "region": { - "message": "Apgabals" + "loggingInOn": { + "message": "Piesakās" }, - "eu": { - "message": "ES", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ASV", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Pašizvietots" diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 98bb03ef7d4..63e8c9bfa2e 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 18292129999..94d2c8f37c5 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index ceeb67d0f74..69b25d3e763 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 9123788514f..85683848236 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 9e7834e88f1..257c86c4c34 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aanbevolen instellingen" }, - "region": { - "message": "Regio" + "loggingInOn": { + "message": "Inloggen op" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Zelfgehost" diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index d0750b4d2c1..e80dc037e49 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 02495ffc371..d0a30e5e327 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 2cd26384181..68c3bec739f 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aktualizacja ustawień zalecanych" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logowanie do" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Samodzielnie hostowany" diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 6db0402ba44..6af040aa65a 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 820b3810635..523af403436 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Atualização de definições recomendadas" }, - "region": { - "message": "Região" + "loggingInOn": { + "message": "A iniciar sessão em" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "EUA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Auto-hospedado" diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index e0babc049c9..2ea79931dfd 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 0dfccf048be..4ee6c0146a2 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Рекомендуемое обновление настроек" }, - "region": { - "message": "Регион" + "loggingInOn": { + "message": "Войти на" }, - "eu": { - "message": "Европа", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "США", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Собственный хостинг" diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 7fe72ca1226..dd521ed5e5d 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 30418ea8ca6..752ef8e44fb 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Odporúčaná aktualizácia nastavenia" }, - "region": { - "message": "Región" + "loggingInOn": { + "message": "Prihlásenie na" }, - "eu": { - "message": "EÚ", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Vlastný hosting" diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 5d4f40ed148..2dd3e77b911 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index cea5d01ff98..e77a89fe2c7 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Препоручено ажурирање поставки" }, - "region": { - "message": "Регион" + "loggingInOn": { + "message": "Пријављено на" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Личан хостинг" diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 7eae768b776..01fe1731e00 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logga in på" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index ed828fa2255..ede96425e9c 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 45c05455bae..7221191a142 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 9e98dbee2c3..340dfd753ab 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Önerilen Ayarlar Güncellemesi" }, - "region": { - "message": "Bölge" + "loggingInOn": { + "message": "Giriş yapılan konum" }, - "eu": { - "message": "AB", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ABD", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Barındırılan" diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index ed4da8b553a..5b9afdc1000 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Оновлення рекомендованих налаштувань" }, - "region": { - "message": "Регіон" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "ЄС", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "США", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Власне розміщення" diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index e8b951fd61a..6f69be27586 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "Self-hosted" diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 22167a606d1..8e03d9521fe 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "推荐的设置更新" }, - "region": { - "message": "区域" + "loggingInOn": { + "message": "登录到" }, - "eu": { - "message": "欧盟", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "美国", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "自托管" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 104402b5d10..bf0ce480427 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -2252,16 +2252,14 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, - "region": { - "message": "區域" + "loggingInOn": { + "message": "Logging in on" }, - "eu": { - "message": "歐洲 (EU)", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "美國 (US)", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "selfHosted": { "message": "自建" From 419cd9b62bf0444aa430fc3c9438ab52dadfaa3f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:09:40 +0000 Subject: [PATCH 011/135] Autosync the updated translations (#6013) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 70 ++++++++++--------- apps/browser/src/_locales/az/messages.json | 24 ++++--- apps/browser/src/_locales/be/messages.json | 20 +++--- apps/browser/src/_locales/bg/messages.json | 20 +++--- apps/browser/src/_locales/bn/messages.json | 20 +++--- apps/browser/src/_locales/bs/messages.json | 20 +++--- apps/browser/src/_locales/ca/messages.json | 20 +++--- apps/browser/src/_locales/cs/messages.json | 20 +++--- apps/browser/src/_locales/cy/messages.json | 20 +++--- apps/browser/src/_locales/da/messages.json | 20 +++--- apps/browser/src/_locales/de/messages.json | 30 ++++---- apps/browser/src/_locales/el/messages.json | 20 +++--- apps/browser/src/_locales/en_GB/messages.json | 20 +++--- apps/browser/src/_locales/en_IN/messages.json | 20 +++--- apps/browser/src/_locales/es/messages.json | 20 +++--- apps/browser/src/_locales/et/messages.json | 20 +++--- apps/browser/src/_locales/eu/messages.json | 20 +++--- apps/browser/src/_locales/fa/messages.json | 20 +++--- apps/browser/src/_locales/fi/messages.json | 20 +++--- apps/browser/src/_locales/fil/messages.json | 20 +++--- apps/browser/src/_locales/fr/messages.json | 20 +++--- apps/browser/src/_locales/gl/messages.json | 20 +++--- apps/browser/src/_locales/he/messages.json | 20 +++--- apps/browser/src/_locales/hi/messages.json | 20 +++--- apps/browser/src/_locales/hr/messages.json | 20 +++--- apps/browser/src/_locales/hu/messages.json | 20 +++--- apps/browser/src/_locales/id/messages.json | 20 +++--- apps/browser/src/_locales/it/messages.json | 20 +++--- apps/browser/src/_locales/ja/messages.json | 20 +++--- apps/browser/src/_locales/ka/messages.json | 20 +++--- apps/browser/src/_locales/km/messages.json | 20 +++--- apps/browser/src/_locales/kn/messages.json | 20 +++--- apps/browser/src/_locales/ko/messages.json | 20 +++--- apps/browser/src/_locales/lt/messages.json | 28 ++++---- apps/browser/src/_locales/lv/messages.json | 20 +++--- apps/browser/src/_locales/ml/messages.json | 20 +++--- apps/browser/src/_locales/mr/messages.json | 20 +++--- apps/browser/src/_locales/my/messages.json | 20 +++--- apps/browser/src/_locales/nb/messages.json | 20 +++--- apps/browser/src/_locales/ne/messages.json | 20 +++--- apps/browser/src/_locales/nl/messages.json | 20 +++--- apps/browser/src/_locales/nn/messages.json | 20 +++--- apps/browser/src/_locales/or/messages.json | 20 +++--- apps/browser/src/_locales/pl/messages.json | 20 +++--- apps/browser/src/_locales/pt_BR/messages.json | 20 +++--- apps/browser/src/_locales/pt_PT/messages.json | 20 +++--- apps/browser/src/_locales/ro/messages.json | 20 +++--- apps/browser/src/_locales/ru/messages.json | 20 +++--- apps/browser/src/_locales/si/messages.json | 20 +++--- apps/browser/src/_locales/sk/messages.json | 20 +++--- apps/browser/src/_locales/sl/messages.json | 20 +++--- apps/browser/src/_locales/sr/messages.json | 20 +++--- apps/browser/src/_locales/sv/messages.json | 20 +++--- apps/browser/src/_locales/te/messages.json | 20 +++--- apps/browser/src/_locales/th/messages.json | 20 +++--- apps/browser/src/_locales/tr/messages.json | 20 +++--- apps/browser/src/_locales/uk/messages.json | 20 +++--- apps/browser/src/_locales/vi/messages.json | 20 +++--- apps/browser/src/_locales/zh_CN/messages.json | 20 +++--- apps/browser/src/_locales/zh_TW/messages.json | 20 +++--- apps/browser/store/locales/de/copy.resx | 2 +- apps/browser/store/locales/lt/copy.resx | 2 +- 62 files changed, 758 insertions(+), 518 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 0e42d032fbb..bf021b10513 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "تحديث" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "إظهار خيارات قائمة السياق" }, @@ -1444,34 +1450,34 @@ "message": "هل أنت متأكد من أنك تريد استعادة هذا العنصر؟" }, "restoredItem": { - "message": "Item restored" + "message": "تم استعادة العنصر" }, "vaultTimeoutLogOutConfirmation": { - "message": "Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?" + "message": "سيؤدي تسجيل الخروج إلى إزالة جميع إمكانية الوصول إلى خزنتك ويتطلب المصادقة عبر الإنترنت بعد انتهاء المهلة. هل أنت متأكد من أنك تريد استخدام هذا الإعداد؟" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Timeout action confirmation" + "message": "تأكيد إجراء المهلة" }, "autoFillAndSave": { - "message": "Auto-fill and save" + "message": "التعبئة التلقائية والحفظ" }, "autoFillSuccessAndSavedUri": { - "message": "Item auto-filled and URI saved" + "message": "تم تعبئة العنصر تلقائياً وحفظ عنوان URI" }, "autoFillSuccess": { - "message": "Item auto-filled " + "message": "ملء العنصر تلقائياً " }, "insecurePageWarning": { - "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + "message": "تحذير: هذه صفحة HTTP غير آمنة، وأي معلومات تقدمها يمكن رؤيتها وتغييرها من قبل الآخرين. تم حفظ تسجيل الدخول هذا في الأصل على صفحة آمنة (HTTPS)." }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "هل مازلت ترغب في ملء هذا الدخول؟" }, "autofillIframeWarning": { - "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + "message": "يتم استضافة النموذج من قبل نطاق مختلف عن عنوان URI الخاص بتسجيل الدخول المحفوظ. اختر موافق للملء التلقائي على أي حال، أو ألغ للتوقف." }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "لمنع هذا التحذير في المستقبل، حفظ هذا الرابط، $HOSTNAME$ إلى عنصر تسجيل الدخول الخاص بك Bitwarden لهذا الموقع.", "placeholders": { "hostname": { "content": "$1", @@ -1480,22 +1486,22 @@ } }, "setMasterPassword": { - "message": "Set master password" + "message": "تعيين كلمة مرور رئيسية" }, "currentMasterPass": { - "message": "Current master password" + "message": "كلمة المرور الرئيسية الحالية" }, "newMasterPass": { - "message": "New master password" + "message": "كلمة مرور رئيسية جديدة" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "تأكيد كلمة المرور الرئيسية الجديدة" }, "masterPasswordPolicyInEffect": { - "message": "One or more organization policies require your master password to meet the following requirements:" + "message": "1 - تتطلب سياسة واحدة أو أكثر من سياسات المؤسسة كلمة مرورك الرئيسية لتلبية المتطلبات التالية:" }, "policyInEffectMinComplexity": { - "message": "Minimum complexity score of $SCORE$", + "message": "الحد الأدنى لدرجة التعقيد $SCORE$", "placeholders": { "score": { "content": "$1", @@ -1504,7 +1510,7 @@ } }, "policyInEffectMinLength": { - "message": "Minimum length of $LENGTH$", + "message": "الحد الأدنى لطول $LENGTH$", "placeholders": { "length": { "content": "$1", @@ -1513,16 +1519,16 @@ } }, "policyInEffectUppercase": { - "message": "Contain one or more uppercase characters" + "message": "يحتوي على حرف كبير واحد أو أكثر" }, "policyInEffectLowercase": { - "message": "Contain one or more lowercase characters" + "message": "يحتوي على واحد أو أكثر من الأحرف الصغيرة" }, "policyInEffectNumbers": { - "message": "Contain one or more numbers" + "message": "يحتوي على رقم واحد أو أكثر" }, "policyInEffectSpecial": { - "message": "Contain one or more of the following special characters $CHARS$", + "message": "يحتوي على واحد أو أكثر من الأحرف الخاصة التالية $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -1534,7 +1540,7 @@ "message": "كلمة المرور الرئيسية الجديدة لا تفي بمتطلبات السياسة العامة." }, "acceptPolicies": { - "message": "By checking this box you agree to the following:" + "message": "من خلال تحديد هذا المربع فإنك توافق على ما يلي:" }, "acceptPoliciesRequired": { "message": "Terms of Service and Privacy Policy have not been acknowledged." @@ -1618,13 +1624,13 @@ "message": "An organization policy is affecting your ownership options." }, "excludedDomains": { - "message": "Excluded domains" + "message": "النطاقات المستبعدة" }, "excludedDomainsDesc": { - "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." + "message": "Bitwarden لن يطلب حفظ تفاصيل تسجيل الدخول لهذه النطاقات. يجب عليك تحديث الصفحة حتى تصبح التغييرات سارية المفعول." }, "excludedDomainsInvalidDomain": { - "message": "$DOMAIN$ is not a valid domain", + "message": "$DOMAIN$ نطاق غير صالح", "placeholders": { "domain": { "content": "$1", @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index cbae4e49090..26d26ac38a2 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Güncəllə" }, + "notificationUnlockDesc": { + "message": "Avto-doldurma tələblərini tamamlamaq üçün Bitwarden anbarınızın kilidini açın." + }, + "notificationUnlock": { + "message": "Kilidi aç" + }, "enableContextMenuItem": { "message": "Konteks menyu seçimlərini göstər" }, @@ -2221,27 +2227,25 @@ } } }, - "region": { - "message": "Bölgə" + "loggingInOn": { + "message": "Giriş edilir" }, "opensInANewWindow": { "message": "Yeni bir pəncərədə açılır" }, - "eu": { - "message": "AB", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ABŞ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Müraciət rədd edildi. Bu səhifəyə baxmaq üçün icazəniz yoxdur." }, "general": { - "message": "General" + "message": "Ümumi" }, "display": { - "message": "Display" + "message": "Ekran" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 8080b7c8ded..946b2dc5d9d 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Абнавіць" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Паказваць параметры кантэкстнага меню" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Рэгіён" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Адкрываць у новым акне" }, - "eu": { - "message": "ЕС", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ЗША", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Доступ забаронены. У вас не дастаткова правоў для прагляду гэтай старонкі." diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index a5eda0555c0..04a9f08050d 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Да, нека се обнови сега" }, + "notificationUnlockDesc": { + "message": "Отключете трезора си в Битуорден, за да завършите заявката за автоматично попълване." + }, + "notificationUnlock": { + "message": "Отключване" + }, "enableContextMenuItem": { "message": "Показване на опции в контекстното меню" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Регион" + "loggingInOn": { + "message": "Вписване в" }, "opensInANewWindow": { "message": "Отваря се в нов прозорец" }, - "eu": { - "message": "ЕС", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "САЩ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Отказан достъп. Нямате право за преглед на страницата." diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index d5f735eae5a..aef8c25bfeb 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "হ্যাঁ, এখনই হালনাগাদ করুন" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index f96678dd6f1..4f85dde0c37 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 2ea5e072025..916eeea79f4 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Actualitza" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Mostra les opcions del menú contextual" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Regió" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "S'obri en una finestra nova" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "EUA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Accés denegat. No teniu permís per veure aquesta pàgina." diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 859a8f49a1a..de48e4c237d 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Aktualizovat" }, + "notificationUnlockDesc": { + "message": "Pro dokončení požadavku na automatické vyplnění odemkněte Váš trezor na Bitwardenu." + }, + "notificationUnlock": { + "message": "Odemknout" + }, "enableContextMenuItem": { "message": "Zobrazit volby v kontextovém menu" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Přihlašování na" }, "opensInANewWindow": { "message": "Otevře se v novém okně" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Přístup byl odepřen. Nemáte oprávnění k zobrazení této stránky." diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 6a651cad43c..2d0dc622f72 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Diweddaru" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "UDA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Mynediad wedi ei wrthod. Does gennych chi ddim caniatâd i weld y dudalen hon." diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 8c3a4c6856a..58dbe45aec2 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Opdatér" }, + "notificationUnlockDesc": { + "message": "Oplås din Bitwarden boks for at færdiggøre autoudfyldanmodningen." + }, + "notificationUnlock": { + "message": "Oplås" + }, "enableContextMenuItem": { "message": "Vis indstillinger i kontekstmenuen" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logger ind på" }, "opensInANewWindow": { "message": "Åbnes i et nyt vindue" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Adgang nægtet. Nødvendig tilladelse til at se siden mangler." diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 374130ffbc5..d48d1ec04c2 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -11,7 +11,7 @@ "description": "Extension description" }, "loginOrCreateNewAccount": { - "message": "Du musst dich anmelden oder einen neuen Account erstellen, um auf den Tresor zugreifen zu können." + "message": "Melde dich an oder erstelle ein neues Konto, um auf deinen Tresor zuzugreifen." }, "createAccount": { "message": "Konto erstellen" @@ -294,7 +294,7 @@ "message": "Eintragsinformationen" }, "username": { - "message": "Nutzername" + "message": "Benutzername" }, "password": { "message": "Passwort" @@ -357,7 +357,7 @@ "message": "Entsperren" }, "loggedInAsOn": { - "message": "Eingeloggt als $EMAIL$ auf $HOSTNAME$.", + "message": "Angemeldet als $EMAIL$ auf $HOSTNAME$.", "placeholders": { "email": { "content": "$1", @@ -409,7 +409,7 @@ "message": "1 Stunde" }, "fourHours": { - "message": "4 Stunde" + "message": "4 Stunden" }, "onLocked": { "message": "Wenn System gesperrt" @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Aktualisieren" }, + "notificationUnlockDesc": { + "message": "Entsperre deinen Bitwarden Tresor, um die Auto-Ausfüllen-Anfrage abzuschließen." + }, + "notificationUnlock": { + "message": "Entsperren" + }, "enableContextMenuItem": { "message": "Kontextmenüoptionen anzeigen" }, @@ -1582,7 +1588,7 @@ "message": "Desktop-Kommunikation unterbrochen" }, "nativeMessagingWrongUserDesc": { - "message": "Die Desktop-Anwendung ist in ein anderes Konto eingeloggt. Bitte stelle sicher, dass beide Anwendungen mit demselben Konto angemeldet sind." + "message": "Die Desktop-Anwendung ist in einem anderen Konto angemeldet. Bitte stelle sicher, dass beide Anwendungen mit demselben Konto angemeldet sind." }, "nativeMessagingWrongUserTitle": { "message": "Konten stimmen nicht überein" @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Anmelden bei" }, "opensInANewWindow": { "message": "Wird in einem neuen Fenster geöffnet" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Zugriff verweigert. Du hast keine Berechtigung, diese Seite anzuzeigen." diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 3279937f18f..9a3ab302bce 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Ναι, Ενημέρωση Τώρα" }, + "notificationUnlockDesc": { + "message": "Ξεκλειδώστε το θησαυ/κιο Bitwarden σας για να ολοκληρώσετε το αίτημα αυτόματης πλήρωσης." + }, + "notificationUnlock": { + "message": "Ξεκλείδωμα" + }, "enableContextMenuItem": { "message": "Εμφάνιση επιλογών μενού περιβάλλοντος" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Σύνδεση ως" }, "opensInANewWindow": { "message": "Ανοίγει σε νέο παράθυρο" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Δεν επιτρέπεται η πρόσβαση. Δεν έχετε άδεια για να δείτε αυτή τη σελίδα." diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 4efd5dafa50..9627efab01d 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 7b960102d9f..5903b01eb3b 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Yes, update now" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 6adc180c988..e9dd0024495 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Actualizar" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Mostrar las opciones de menú contextuales" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Región" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Abre en una nueva ventana" }, - "eu": { - "message": "Unión Europea", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "EE.UU.", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Acceso denegado. No tiene permiso para ver esta página." diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 254c1cefb76..7ae023a60f5 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Jah, uuenda" }, + "notificationUnlockDesc": { + "message": "Ava Bitwardeni hoidla, et automaattäide lõpuni viia." + }, + "notificationUnlock": { + "message": "Lukusta lahti" + }, "enableContextMenuItem": { "message": "Kuva parema kliki menüü valikud" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Piirkond" + "loggingInOn": { + "message": "Sisselogimas kui" }, "opensInANewWindow": { "message": "Avaneb uues aknas" }, - "eu": { - "message": "EL", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Ligipääs keelatud. Sul pole lubatud seda lehekülge vaadata." diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 4523b995642..eaab1c4b4b9 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Eguneratu" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Erakutsi laster-menuko aukerak" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index b938e52a98d..5ebfe79be2b 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "به‌روزرسانی" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "نمایش گزینه‌های منوی زمینه" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "منطقه" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "در پنجره جدید باز می‌شود" }, - "eu": { - "message": "اروپا", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "امریکا", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "دسترسی رد شد. شما اجازه مشاهده این صفحه را ندارید." diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 6fd0ffda2a9..e95aa2bc803 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Päivitä" }, + "notificationUnlockDesc": { + "message": "Viimeistele automaattitäytön pyyntö avaamalla Bitwarden-holvisi lukitus." + }, + "notificationUnlock": { + "message": "Avaa" + }, "enableContextMenuItem": { "message": "Näytä sisältövalikon valinnat" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Alue" + "loggingInOn": { + "message": "Kirjaudutaan sijaintiin" }, "opensInANewWindow": { "message": "Avautuu uudessa ikkunassa" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Pääsy estetty. Sinulla ei ole oikeutta avata sivua." diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 8a23901eb56..c6082c51669 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "I-update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Ipakita ang mga opsyon ng menu ng konteksto" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 37b6bbc0c26..d5216ae72d7 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Mettre à jour" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Déverrouiller" + }, "enableContextMenuItem": { "message": "Afficher les options du menu contextuel" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Région" + "loggingInOn": { + "message": "Connexion sur" }, "opensInANewWindow": { "message": "S'ouvre dans une nouvelle fenêtre" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Accès refusé. Vous n'avez pas l'autorisation de voir cette page." diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 439f3216097..64c8bf51176 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "כן, עדכן עכשיו" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index eb08d59c2af..c91afe71cf3 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Yes, Update Now" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "संदर्भ मेनू विकल्प दिखाएं" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 3b01d015d10..65bbc664fdd 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Ažuriraj" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Prikaži opcije kotekstualnog izbornika" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index e7b04d0b025..bd106eac48d 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Frissítés" }, + "notificationUnlockDesc": { + "message": "A Bitwarden széf feloldása az automatikus kitöltési kérés teljesítéséhez." + }, + "notificationUnlock": { + "message": "Feloldás" + }, "enableContextMenuItem": { "message": "Helyi menü opciók megjelenítése" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Régió" + "loggingInOn": { + "message": "Bejelentkezés:" }, "opensInANewWindow": { "message": "Megnyitás új ablakban" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "A hozzáférés megtagadásra került. Nincs jogosultság az oldal megtekintésére." diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 84c3938f097..724e8bc30d0 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Iya, Perbarui Sekarang" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 53552ae2ffc..20f338b39e1 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Aggiorna" }, + "notificationUnlockDesc": { + "message": "Sblocca la tua cassaforte di Bitwarden per completare la richiesta di riempimento automatico." + }, + "notificationUnlock": { + "message": "Sblocca" + }, "enableContextMenuItem": { "message": "Mostra opzioni nel menu contestuale" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Regione" + "loggingInOn": { + "message": "Accedendo su" }, "opensInANewWindow": { "message": "Si apre in una nuova finestra" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Accesso negato. Non hai i permessi necessari per visualizzare questa pagina." diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 3bfcffd034c..e52752179b7 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "今すぐ更新する" }, + "notificationUnlockDesc": { + "message": "Bitwarden 保管庫をロック解除して自動入力リクエストを完了してください。" + }, + "notificationUnlock": { + "message": "ロック解除" + }, "enableContextMenuItem": { "message": "コンテキストメニューオプションを表示" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "リージョン" + "loggingInOn": { + "message": "ログイン先" }, "opensInANewWindow": { "message": "新しいウィンドウで開く" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "米国", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "アクセスが拒否されました。このページを表示する権限がありません。" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index e13a6c5e038..569277e8e34 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 9cbabce7942..3a9a33dda1a 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "ಹೌದು, ಈಗ ನವೀಕರಿಸಿ" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index c23673320d1..50cadc8a949 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "예, 지금 변경하겠습니다." }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 682cd40c52a..b1dc314612c 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Atnaujinti" }, + "notificationUnlockDesc": { + "message": "Atrakink savo Bitwarden saugyklą, kad užpildytum automatinio užpildymo užklausą." + }, + "notificationUnlock": { + "message": "Atrakinti" + }, "enableContextMenuItem": { "message": "Rodyti kontekstinio meniu pasririnkimus" }, @@ -1537,7 +1543,7 @@ "message": "By checking this box you agree to the following:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Paslaugų teikimo sąlygos ir privatumo politika nebuvo pripažinti." }, "termsOfService": { "message": "Paslaugų teikimo paslaugos" @@ -1552,13 +1558,13 @@ "message": "Gerai" }, "desktopSyncVerificationTitle": { - "message": "Desktop sync verification" + "message": "Darbalaukio sinchronizavimo verifikavimas" }, "desktopIntegrationVerificationText": { - "message": "Please verify that the desktop application shows this fingerprint: " + "message": "Patikrink, ar darbalaukio programoje rodomas šis pirštų atspaudas: " }, "desktopIntegrationDisabledTitle": { - "message": "Browser integration is not set up" + "message": "Naršyklės integracija nėra nustatyta" }, "desktopIntegrationDisabledDesc": { "message": "Browser integration is not set up in the Bitwarden desktop application. Please set it up in the settings within the desktop application." @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "ES", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "JAV", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Prieiga uždrausta. Neturi teisės peržiūrėti šį puslapį." diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 3a13d9872ad..bd42b3bcbbf 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Jā, atjaunināt" }, + "notificationUnlockDesc": { + "message": "Jāatslēdz Bitwarden glabātava, lai pabeigtu automātiskās aizpildīšanas pieprasījumu." + }, + "notificationUnlock": { + "message": "Atslēgt" + }, "enableContextMenuItem": { "message": "Rādīt konteksta izvēlnes iespējas" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Apgabals" + "loggingInOn": { + "message": "Piesakās" }, "opensInANewWindow": { "message": "Atver jaunā logā" }, - "eu": { - "message": "ES", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ASV", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Piekļuve liegta. Nav nepieciešamo atļauju, lai skatītu šo lapu." diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index fd59a7798cb..ad8f6083c22 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "ശരി, ഇപ്പോൾ അപ്ഡേറ്റ് ചെയ്യുക" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 33e843f3b4a..70b41cafbdb 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 596a717c203..0e4904dcd15 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Ja, oppdater nå" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Vis alternativer for kontekstmeny" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 130524de47b..7ff5ec8b36b 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Ja, nu bijwerken" }, + "notificationUnlockDesc": { + "message": "Ontgrendel je Bitwarden-kluis om het auto-invulverzoek te voltooien." + }, + "notificationUnlock": { + "message": "Ontgrendelen" + }, "enableContextMenuItem": { "message": "Contextmenu-opties weergeven" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Regio" + "loggingInOn": { + "message": "Inloggen op" }, "opensInANewWindow": { "message": "Opent in een nieuw venster" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Toegang geweigerd. Je hebt geen toestemming om deze pagina te bekijken." diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index e5ba19c2442..f010699118b 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Zaktualizuj" }, + "notificationUnlockDesc": { + "message": "Odblokuj swój sejf Bitwarden, aby ukończyć żądanie autouzupełniania." + }, + "notificationUnlock": { + "message": "Odblokuj" + }, "enableContextMenuItem": { "message": "Pokaż opcje menu kontekstowego" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logowanie do" }, "opensInANewWindow": { "message": "Otwiera w nowym oknie" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Odmowa dostępu. Nie masz uprawnień do przeglądania tej strony." diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index abcfbeb9e69..d0370af24e9 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Atualizar" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Mostrar opções de menu de contexto" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Região" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Abrir em uma nova janela" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Acesso negado. Você não tem permissão para ver esta página." diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index aa2f50b0e00..5254144a08b 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Atualizar" }, + "notificationUnlockDesc": { + "message": "Desbloqueie o seu cofre Bitwarden para completar o pedido de preenchimento automático." + }, + "notificationUnlock": { + "message": "Desbloquear" + }, "enableContextMenuItem": { "message": "Mostrar opções do menu de contexto" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Região" + "loggingInOn": { + "message": "A iniciar sessão em" }, "opensInANewWindow": { "message": "Abrir numa nova janela" }, - "eu": { - "message": "UE", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "EUA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Acesso negado. Não tem permissão para visualizar esta página." diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 5a8879a4bcd..f4369278d64 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Actualizare" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Afișați opțiunile meniului contextual" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 938c35d1c27..f9c9b0141ab 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Обновить" }, + "notificationUnlockDesc": { + "message": "Разблокируйте свое хранилище Bitwarden для завершения запроса автозаполнения." + }, + "notificationUnlock": { + "message": "Разблокировать" + }, "enableContextMenuItem": { "message": "Показать опции контекстного меню" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Регион" + "loggingInOn": { + "message": "Войти на" }, "opensInANewWindow": { "message": "Откроется в новом окне" }, - "eu": { - "message": "Европа", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "США", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Доступ запрещен. У вас нет разрешения на просмотр этой страницы." diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 5cac9ee12c8..0f209dd000b 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "යාවත්කාල" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 2fdd9c078d1..bccc8a74ce5 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Aktualizovať" }, + "notificationUnlockDesc": { + "message": "Odomknite svoj Bitwarden trezor a dokončite žiadosť o automatické vyplnenie." + }, + "notificationUnlock": { + "message": "Odomknúť" + }, "enableContextMenuItem": { "message": "Zobraziť možnosti kontextovej ponuky" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Región" + "loggingInOn": { + "message": "Prihlásenie na" }, "opensInANewWindow": { "message": "Otvárať v novom okne" }, - "eu": { - "message": "EÚ", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Prístup zamietnutý. Nemáte oprávnenie na zobrazenie tejto stránky." diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index e7b9b7b8a71..64f3415ae27 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Da, posodobi zdaj" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Prikaži možnosti kontekstnega menuja" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Odpre se v novem oknu" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 12d1c66da81..38ba6e1a4b1 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Ажурирај" }, + "notificationUnlockDesc": { + "message": "Откључати Bitwarden сеф да би извршили ауто-пуњење." + }, + "notificationUnlock": { + "message": "Откључај" + }, "enableContextMenuItem": { "message": "Прикажи контекстни мени" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Регион" + "loggingInOn": { + "message": "Пријављено на" }, "opensInANewWindow": { "message": "Отвара се у новом прозору" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Одбијен приступ. Немате дозволу да видите ову страницу." diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 21291110b20..36ebb906412 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Uppdatera" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Visa alternativ för snabbmenyn" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logga in på" }, "opensInANewWindow": { "message": "Öppnas i ett nytt fönster" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "USA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 9e46b55f19e..63dd227e52b 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Update" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Show context menu options" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 2b93ace3a04..667c26a1e12 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Yes, Update Now" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "แสดงตัวเลือกเมนูบริบท" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index f438c3baa91..23727c5af9a 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Güncelle" }, + "notificationUnlockDesc": { + "message": "Otomatik doldurma isteğini tamamlamak için Bitwarden kasanızın kilidini açın." + }, + "notificationUnlock": { + "message": "Kilidi aç" + }, "enableContextMenuItem": { "message": "Bağlam menüsü seçeneklerini göster" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Bölge" + "loggingInOn": { + "message": "Giriş yapılan konum" }, "opensInANewWindow": { "message": "Yeni pencerede açılır" }, - "eu": { - "message": "AB", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "ABD", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Erişim engellendi. Bu sayfayı görüntüleme iznine sahip değilsiniz." diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index f9459c393a5..45177b5bdea 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Оновити" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Показувати в контекстному меню" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Регіон" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Відкривається у новому вікні" }, - "eu": { - "message": "ЄС", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "США", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Доступ заборонено. У вас немає дозволу на перегляд цієї сторінки." diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 35234feeb38..0bb3aaefb85 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "Cập nhật" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "Hiển thị tuỳ chọn menu ngữ cảnh" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "Region" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "Opens in a new window" }, - "eu": { - "message": "EU", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index e4a970a45d5..4f5d42ef1ad 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "更新" }, + "notificationUnlockDesc": { + "message": "解锁 Bitwarden 密码库以完成自动填充请求。" + }, + "notificationUnlock": { + "message": "解锁​​​​" + }, "enableContextMenuItem": { "message": "显示上下文菜单选项" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "区域" + "loggingInOn": { + "message": "登录到" }, "opensInANewWindow": { "message": "在新窗口中打开" }, - "eu": { - "message": "欧盟", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "美国", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "访问被拒绝。您没有权限查看此页面。" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index e2070bdd316..c7350529d77 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -630,6 +630,12 @@ "notificationChangeSave": { "message": "更新" }, + "notificationUnlockDesc": { + "message": "Unlock your Bitwarden vault to complete the auto-fill request." + }, + "notificationUnlock": { + "message": "Unlock" + }, "enableContextMenuItem": { "message": "顯示內容選單選項" }, @@ -2221,19 +2227,17 @@ } } }, - "region": { - "message": "區域" + "loggingInOn": { + "message": "Logging in on" }, "opensInANewWindow": { "message": "在新視窗開啟" }, - "eu": { - "message": "歐洲 (EU)", - "description": "European Union" + "usDomain": { + "message": "bitwarden.com" }, - "us": { - "message": "美國 (US)", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "accessDenied": { "message": "拒絕存取。您沒有檢視此頁面的權限。" diff --git a/apps/browser/store/locales/de/copy.resx b/apps/browser/store/locales/de/copy.resx index 026999c3107..139a6026fdf 100644 --- a/apps/browser/store/locales/de/copy.resx +++ b/apps/browser/store/locales/de/copy.resx @@ -139,7 +139,7 @@ Bitwarden bietet Teams und Enterprise Pläne für Unternehmen an, damit du Passw Warum Bitwarden: Weltklasse Verschlüsselung -Passwörter sind durch erweiterte Ende-zu-Ende-Verschlüsselung (AES-256 Bit, salted hashtag und PBKDF2 SHA-256) so bleiben deine Daten sicher und privat. +Passwörter sind durch erweiterte Ende-zu-Ende-Verschlüsselung (AES-256 Bit, salted hashing und PBKDF2 SHA-256) so bleiben deine Daten sicher und privat. Integrierter Passwortgenerator Generiere starke, einzigartige und zufällige Passwörter basierend auf Sicherheitsanforderungen für jede Website, die du häufig besuchst. diff --git a/apps/browser/store/locales/lt/copy.resx b/apps/browser/store/locales/lt/copy.resx index 730bb942e4f..01bd250546f 100644 --- a/apps/browser/store/locales/lt/copy.resx +++ b/apps/browser/store/locales/lt/copy.resx @@ -124,7 +124,7 @@ Saugi ir nemokama slaptažodžių tvarkyklė visiems įrenginiams - „Bitwarden, Inc.“ yra pagrindinė 8bit Solutions LLC įmonė. + Bitwarden, Inc. yra pagrindinė 8bit Solutions LLC įmonė. ĮVARDINTA GERIAUSIU SLAPTAŽODŽIŲ TVARKYTOJU. From 1046cac33c9967dda26e6e84954866faa0f15324 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 12:12:03 +0000 Subject: [PATCH 012/135] Autosync the updated translations (#6014) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 87 +++++++++++++--- apps/web/src/locales/ar/messages.json | 125 ++++++++++++++++------- apps/web/src/locales/az/messages.json | 103 ++++++++++++++----- apps/web/src/locales/be/messages.json | 87 +++++++++++++--- apps/web/src/locales/bg/messages.json | 89 ++++++++++++---- apps/web/src/locales/bn/messages.json | 87 +++++++++++++--- apps/web/src/locales/bs/messages.json | 87 +++++++++++++--- apps/web/src/locales/ca/messages.json | 91 +++++++++++++---- apps/web/src/locales/cs/messages.json | 85 ++++++++++++--- apps/web/src/locales/cy/messages.json | 87 +++++++++++++--- apps/web/src/locales/da/messages.json | 87 +++++++++++++--- apps/web/src/locales/de/messages.json | 97 ++++++++++++++---- apps/web/src/locales/el/messages.json | 87 +++++++++++++--- apps/web/src/locales/en_GB/messages.json | 87 +++++++++++++--- apps/web/src/locales/en_IN/messages.json | 87 +++++++++++++--- apps/web/src/locales/eo/messages.json | 87 +++++++++++++--- apps/web/src/locales/es/messages.json | 87 +++++++++++++--- apps/web/src/locales/et/messages.json | 87 +++++++++++++--- apps/web/src/locales/eu/messages.json | 87 +++++++++++++--- apps/web/src/locales/fa/messages.json | 87 +++++++++++++--- apps/web/src/locales/fi/messages.json | 93 +++++++++++++---- apps/web/src/locales/fil/messages.json | 87 +++++++++++++--- apps/web/src/locales/fr/messages.json | 87 +++++++++++++--- apps/web/src/locales/gl/messages.json | 87 +++++++++++++--- apps/web/src/locales/he/messages.json | 87 +++++++++++++--- apps/web/src/locales/hi/messages.json | 87 +++++++++++++--- apps/web/src/locales/hr/messages.json | 87 +++++++++++++--- apps/web/src/locales/hu/messages.json | 87 +++++++++++++--- apps/web/src/locales/id/messages.json | 87 +++++++++++++--- apps/web/src/locales/it/messages.json | 87 +++++++++++++--- apps/web/src/locales/ja/messages.json | 91 +++++++++++++---- apps/web/src/locales/ka/messages.json | 87 +++++++++++++--- apps/web/src/locales/km/messages.json | 87 +++++++++++++--- apps/web/src/locales/kn/messages.json | 87 +++++++++++++--- apps/web/src/locales/ko/messages.json | 87 +++++++++++++--- apps/web/src/locales/lv/messages.json | 91 +++++++++++++---- apps/web/src/locales/ml/messages.json | 87 +++++++++++++--- apps/web/src/locales/mr/messages.json | 87 +++++++++++++--- apps/web/src/locales/my/messages.json | 87 +++++++++++++--- apps/web/src/locales/nb/messages.json | 87 +++++++++++++--- apps/web/src/locales/ne/messages.json | 87 +++++++++++++--- apps/web/src/locales/nl/messages.json | 85 ++++++++++++--- apps/web/src/locales/nn/messages.json | 87 +++++++++++++--- apps/web/src/locales/or/messages.json | 87 +++++++++++++--- apps/web/src/locales/pl/messages.json | 87 +++++++++++++--- apps/web/src/locales/pt_BR/messages.json | 87 +++++++++++++--- apps/web/src/locales/pt_PT/messages.json | 87 +++++++++++++--- apps/web/src/locales/ro/messages.json | 87 +++++++++++++--- apps/web/src/locales/ru/messages.json | 91 +++++++++++++---- apps/web/src/locales/si/messages.json | 87 +++++++++++++--- apps/web/src/locales/sk/messages.json | 85 ++++++++++++--- apps/web/src/locales/sl/messages.json | 87 +++++++++++++--- apps/web/src/locales/sr/messages.json | 107 ++++++++++++++----- apps/web/src/locales/sr_CS/messages.json | 87 +++++++++++++--- apps/web/src/locales/sv/messages.json | 99 +++++++++++++----- apps/web/src/locales/te/messages.json | 87 +++++++++++++--- apps/web/src/locales/th/messages.json | 87 +++++++++++++--- apps/web/src/locales/tr/messages.json | 87 +++++++++++++--- apps/web/src/locales/uk/messages.json | 91 +++++++++++++---- apps/web/src/locales/vi/messages.json | 87 +++++++++++++--- apps/web/src/locales/zh_CN/messages.json | 109 +++++++++++++++----- apps/web/src/locales/zh_TW/messages.json | 87 +++++++++++++--- 62 files changed, 4410 insertions(+), 1124 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 2b11b7eab71..7b62d7e8932 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "'n Onverwagte fout het voorgekom." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-posadres" }, "yourVaultIsLocked": { "message": "U kluis is vergrendel. Verifieer u hoofwagwoord om voort te gaan." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Ontgrendel" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopieer bevestigingskode" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Waarskuwing" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Kies die invoerlêer se formaat" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Hierdie item het ou lêeraanhegsels wat herstel moet word." }, - "attachmentFixDesc": { - "message": "Dit is ’n ou lêeraanhegsel wat herstel moet word. Klik om meer uit te vind." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Herstel", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Slegs die organisasiekluisitems wat met $ORGANIZATION$ verbind word, word uitgestuur. Persoonlike kluisitems word nie ingesluit nie.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Streek" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "VS", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "U het nie toestemming om hierdie projek te skrap nie", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index 2fa2138bd9f..ddcc0fa4765 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -216,17 +216,17 @@ "message": "إظهار / إخفاء" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "تبديل الطي", "description": "Toggling an expand/collapse state." }, "generatePassword": { "message": "توليد كلمة مرور" }, "checkPassword": { - "message": "Check if password has been exposed." + "message": "تحقق مما إذا تم الكشف عن كلمة المرور." }, "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "message": "تم الكشف عن كلمة المرور هذه $VALUE$ مرّة (ات) في خروقات البيانات. يجب عليك تغييرها.", "placeholders": { "value": { "content": "$1", @@ -271,29 +271,29 @@ "message": "بحث في المفضلة" }, "searchLogin": { - "message": "Search logins", + "message": "البحث عن تسجيلات الدخول", "description": "Search Login type" }, "searchCard": { - "message": "Search cards", + "message": "البحث عن بطاقات", "description": "Search Card type" }, "searchIdentity": { - "message": "Search identities", + "message": "البحث عن الهويات", "description": "Search Identity type" }, "searchSecureNote": { - "message": "Search secure notes", + "message": "البحث عن ملاحظات آمنة", "description": "Search Secure Note type" }, "searchVault": { "message": "البحث في الخزنة" }, "searchMyVault": { - "message": "Search my vault" + "message": "البحث في خزنتي" }, "searchOrganization": { - "message": "Search organization" + "message": "البحث عن المؤسسة" }, "searchMembers": { "message": "البحث في الأعضاء" @@ -467,7 +467,7 @@ "message": "عناصر الخزنة" }, "filter": { - "message": "Filter" + "message": "تصفية" }, "moveSelectedToOrg": { "message": "نقل العناصر المختارة إلى منظمة" @@ -518,7 +518,7 @@ "message": "تم تعديل العنصر" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ انتقل إلى $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -576,7 +576,7 @@ "message": "تم حذف المجلد" }, "editInfo": { - "message": "Edit info" + "message": "تعديل المعلومات" }, "access": { "message": "وصول" @@ -609,7 +609,7 @@ "message": "تسجيل الدخول باستخدام جهاز" }, "loginWithDeviceEnabledNote": { - "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" + "message": "تسجيل الدخول باستخدام الجهاز يجب أن يتم إعداده في إعدادات تطبيق Bitwarden. هل تحتاج إلى خيار آخر؟" }, "loginWithMasterPassword": { "message": "تسجيل الدخول باستخدام كلمة المرور الرئيسية" @@ -645,10 +645,10 @@ "message": "كلمة المرور الرئيسية" }, "masterPassDesc": { - "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + "message": "كلمة المرور الرئيسية هي كلمة المرور التي تستخدمها للوصول إلى خزنتك. من المهم جدا ألا تنسى كلمة المرور الرئيسية. لا توجد طريقة لاسترداد كلمة المرور في حال نسيتها." }, "masterPassImportant": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "لا يمكن استعادة كلمة المرور الرئيسية إذا نسيتها!" }, "masterPassHintDesc": { "message": "يمكن أن يساعدك تلميح كلمة المرور الرئيسية في تذكر كلمة المرور الخاصة بك في حال نسيتها." @@ -669,7 +669,7 @@ "message": "تلميح كلمة المرور" }, "enterEmailToGetHint": { - "message": "Enter your account email address to receive your master password hint." + "message": "أدخل عنوان البريد الإلكتروني لحسابك للحصول على تلميح كلمة المرور الرئيسية." }, "getMasterPasswordHint": { "message": "احصل على تلميح لكلمة المرور الرئيسية" @@ -687,7 +687,7 @@ "message": "مطلوب إعادة إدخال كلمة المرور الرئيسية." }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "كلمة المرور الرئيسية يجب أن تكون على الأقل $VALUE$ حرفاً.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -706,17 +706,23 @@ "message": "تم إنشاء الحساب بنجاح." }, "masterPassSent": { - "message": "We've sent you an email with your master password hint." + "message": "لقد أرسلنا لك رسالة بريد إلكتروني تحتوي على تلميح كلمة المرور الرئيسية." }, "unexpectedError": { "message": "حدث خطأ غير متوقع." }, + "expirationDateError": { + "message": "الرجاء تحديد تاريخ انتهاء الصلاحية في المستقبل." + }, "emailAddress": { "message": "عنوان البريد الإلكتروني" }, "yourVaultIsLocked": { "message": "خزنتك مقفلة. تحقق من كلمة المرور الرئيسية للمتابعة." }, + "uuid": { + "message": "معرف المستخدم الحالي" + }, "unlock": { "message": "إلغاء القفل" }, @@ -737,7 +743,7 @@ "message": "كلمة المرور الرئيسية غير صالحة" }, "invalidFilePassword": { - "message": "Invalid file password, please use the password you entered when you created the export file." + "message": "كلمة مرور الملف غير صالحة، الرجاء استخدام كلمة المرور التي أدخلتها عند إنشاء ملف التصدير." }, "lockNow": { "message": "قفل الآن" @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "نسخ رمز التحقق" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "تحذير" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "حدد تنسيق ملف الاستيراد" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 1a3813eaaa2..baf9cc7de75 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Gözlənilməz bir səhv baş verdi." }, + "expirationDateError": { + "message": "Zəhmət olmasa gələcəkdə son istifadə tarixi seçin." + }, "emailAddress": { "message": "E-poçt ünvanı" }, "yourVaultIsLocked": { "message": "Anbarınız kilidlənib. Davam etmək üçün ana parolunuzu təsdiqləyin." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Kilidi aç" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Təsdiqləmə kodunu kopyala" }, + "copyUuid": { + "message": "UUID-ni kopyala" + }, "warning": { "message": "Xəbərdarlıq" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "İxrac edilən faylın parolu açılarkən xəta baş verdi. Şifrələmə açarınız, verilənlərin ixracında istifadə olunan şifrələmə açarı ilə uyğunlaşmır." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Bir qovluq seçin" + }, + "selectImportCollection": { + "message": "Bir kolleksiya seçin" + }, + "importTargetHint": { + "message": "Daxilə köçürülən fayl məzmununun $DESTINATION$ yerinə daşınmasını istəyirsinizsə bu variantı seçin", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Faylda təyin edilməmiş elementlər var." + }, "selectFormat": { "message": "İdxal faylının formatını seçin" }, @@ -2636,7 +2670,7 @@ "message": "Veb anbar" }, "bitWebVault": { - "message": "Bitwarden Web vault" + "message": "Bitwarden Web anbarı" }, "bitSecretsManager": { "message": "Bitwarden Secrets Manager" @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Bu element, düzəldilməli köhnə fayl qoşmalarını ehtiva edir." }, - "attachmentFixDesc": { - "message": "Bu, düzəldilməli köhnə bir fayl qoşmasıdır. Daha ətraflı öyrənmək üçün klikləyin." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Düzəlt", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Yalnız $ORGANIZATION$ ilə əlaqəli təşkilat anbar elementləri ixrac ediləcək. Şəxsi anbar elementləri və digər təşkilatlardan olan elementlər daxil edilmir.", + "exportingOrganizationVaultDesc": { + "message": "Yalnız $ORGANIZATION$ ilə əlaqələndirilmiş təşkilat anbarı ixrac ediləcək. Fərdi anbardakı və digər təşkilat elementlər daxil edilmir.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Mövcud üzvlərin öz parollarını dəyişdirməsini tələb et" }, - "region": { - "message": "Bölgə" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "AB", - "description": "European Union" - }, - "us": { - "message": "ABŞ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Bu layihəni silmək icazəniz yoxdur", @@ -6825,7 +6854,7 @@ "message": "KDF tənzimləmələrini güncəllə" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Güvənli cihazlar" }, "memberDecryptionTdeDescriptionPartOne": { "message": "Kimlik təsdiqləndikdən sonra üzvlər, cihazlarından saxlanılan açarı istifadə edərək anbar verilənlərinin şifrələrini aça biləcək", @@ -6935,7 +6964,7 @@ "message": "Ana parolu olmayan üzvləri onlar üçün parol tənzimləmədən silmək, tam hesablarına müraciəti məhdudlaşdıra bilər." }, "startYour7DayFreeTrialOfBitwardenFor": { - "message": "Start your 7-Day free trial of Bitwarden for $ORG$", + "message": "$ORG$ üçün Bitwarden 7 günlük ödənişsiz sınağı başladın", "placeholders": { "org": { "content": "$1", @@ -6944,16 +6973,16 @@ } }, "next": { - "message": "Next" + "message": "Növbəti" }, "usFlag": { - "message": "US flag" + "message": "US bayrağı" }, "euFlag": { - "message": "EU flag" + "message": "EU bayrağı" }, "selectedRegionFlag": { - "message": "Selected region flag" + "message": "Seçilmiş bölgə bayrağı" }, "sendsNoItemsTitle": { "message": "Aktiv \"Send\" yoxdur", @@ -6979,7 +7008,7 @@ "message": "For engineering and DevOps teams to manage secrets throughout the software development lifecycle." }, "free2PersonOrganization": { - "message": "Free 2-person Organizations" + "message": "Ödənişsiz 2 nəfərlik Təşkilatlar" }, "unlimitedSecrets": { "message": "Unlimited secrets" @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Əlavə xidmət hesabları" }, - "additionalServiceAccountsDesc": { - "message": "Planınız $COUNT$ xidmət hesabı ilə gəlir. Əlavə xidmət hesablarını ayda $COST$ qarşılığında əlavə edə bilərsiniz.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Aylıq $COST$ qiymətinə əlavə xidmət hesablarını əlavə edə bilərsiniz.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Maksimal mümkün xidmət hesabı qiyməti" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Sonu" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index de48c9cf035..965ca2a12ae 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Адбылася нечаканая памылка." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Адрас электроннай пошты" }, "yourVaultIsLocked": { "message": "Ваша сховішча заблакіравана. Увядзіце асноўны пароль для працягу." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Разблакіраваць" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Скапіяваць праверачны код" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Папярэджанне" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Памылка дэшыфроўкі экспартаванага файла. Ваш ключ шыфравання не супадае з ключом шыфравання, які выкарыстоўваецца для экспартавання даных." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Выберыце фармат файла імпартавання" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Гэты элемент мае старыя далучаныя файлы, якія неабходна выправіць." }, - "attachmentFixDesc": { - "message": "Гэта стары далучаны файл, які неабходна выправіць. Націсніце, каб даведацца больш." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Выправіць", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Будуць экспартаваны толькі запісы сховішча арганізацыі, якія звязаны з $ORGANIZATION$. Элементы асабістага сховішча і элементы з іншых арганізацый не будуць уключаны.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Патрабаваць ад існуючых удзельнікаў змены пароляў" }, - "region": { - "message": "Рэгіён" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "ЕС", - "description": "European Union" - }, - "us": { - "message": "ЗША", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "У вас няма правоў для выдалення гэтага праекта", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Дадатковыя сэрвісныя ўліковыя запісы" }, - "additionalServiceAccountsDesc": { - "message": "Ваш тарыфны план мае наступную колькасць сэрвісных уліковых запісаў: $COUNT$. Вы можаце дадаць дадатковую колькасць сэрвісных уліковых запісаў за $COST$ у месяц.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Максімальны патэнцыйны кошт сэрвісных уліковых запісаў" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index f513bc11aea..19010386404 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Възникна неочаквана грешка." }, + "expirationDateError": { + "message": "Моля, изберете дата на давност, която е в бъдещето." + }, "emailAddress": { "message": "Адрес на електронната поща" }, "yourVaultIsLocked": { "message": "Трезорът е заключен — въведете главната си парола, за да продължите." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Отключване" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Копиране на кода за потвърждаване" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Внимание" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Грешка при дешифрирането на изнесения файл. Ключът за шифриране не отговаря на този, който е използван за изнасянето на данните." }, + "importDestination": { + "message": "Място на внасяне" + }, + "learnAboutImportOptions": { + "message": "Научете повече относно възможностите за внасяне" + }, + "selectImportFolder": { + "message": "Изберете папка" + }, + "selectImportCollection": { + "message": "Изберете колекция" + }, + "importTargetHint": { + "message": "Изберете тази опция, ако искате съдържанието на внесения файл да бъде преместено в $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Файлът съдържа невъзложени елементи." + }, "selectFormat": { "message": "Избор на форма̀та на файла за внасяне" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Този запис съдържа стар, прикачен файл, който трябва да бъде поправен. Натиснете, за да научите повече." }, - "attachmentFixDesc": { - "message": "Този прикачен файл е стар и трябва да бъде поправен. Натиснете, за да научите повече." + "attachmentFixDescription": { + "message": "Този прикачен файл използва остарял метод на шифроване. Избирете „Поправяне“ за да го свалите, пре-шифровате и качите отново." }, "fix": { "message": "Поправяне", @@ -4705,7 +4739,7 @@ "message": "Грешка" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "Управлението на потребителите трябва да бъде дадено заедно с разрешението за Управление на възстановяването на регистрации" }, "setupProvider": { "message": "Настройка на доставчика" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Ще бъдат изнесени само записите от трезора свързан с $ORGANIZATION$. Записите в личния трезор и тези в други организации няма да бъдат включени.", + "exportingOrganizationVaultDesc": { + "message": "Ще бъдат изнесени само записите от трезора свързан с $ORGANIZATION$. Записите в отделните лични трезори и тези в други организации няма да бъдат включени.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Задължаване на текущите членове да сменят паролите си" }, - "region": { - "message": "Регион" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "ЕС", - "description": "European Union" - }, - "us": { - "message": "САЩ", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Нямате право за изтриване на този проект", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Допълнителни сервизни акаунти" }, - "additionalServiceAccountsDesc": { - "message": "Планът Ви включва $COUNT$ сервизни акаунта. Можете да добавите още за $COST$ на месец.", + "includedServiceAccounts": { + "message": "В плана Ви са включени $COUNT$ сервизни акаунта.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Може да добавите още сервизни акаунти за $COST$ на месец.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Максимална възможна цена за сервизни акаунти" + }, + "smBetaEndedDesc": { + "message": "Бета-версията на Управлението на тайни приключи на $BETA_ENDING_DATE$. Остават $DAYS$ дни да добавите Управлението на тайни към своя платен абонамент, за да запазите достъпа си до създадените данни. Свържете се с поддръжката, ако искате да добавите Управлението на тайни към абонамента си.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Бета-версията приключва" + }, + "beta": { + "message": "Бета" } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 26d1b51d9fc..be5de14b639 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "একটি অপ্রত্যাশিত ত্রুটি ঘটেছে।" }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "ইমেইল ঠিকানা" }, "yourVaultIsLocked": { "message": "আপনার ভল্ট লক করা আছে। চালিয়ে যেতে আপনার মূল পাসওয়ার্ডটি যাচাই করান।" }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "আনলক" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 8d8b073aae4..2780926fbe4 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email adresa" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Otključaj" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 1621adc8337..8f38e86ec2f 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "S'ha produït un error inesperat." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Adreça electrònica" }, "yourVaultIsLocked": { "message": "La caixa forta està bloquejada. Verifiqueu la contrasenya mestra per continuar." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Desbloqueja" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copia el codi de verificació" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Advertiment" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error en desxifrar el fitxer exportat. La vostra clau de xifratge no coincideix amb la clau de xifratge utilitzada per exportar les dades." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Seleccioneu el format del fitxer d'importació" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Aquest element té fitxers adjunts antics que s'han de corregir." }, - "attachmentFixDesc": { - "message": "Es tracta d'un arxiu adjunt antic que cal corregir. Feu clic per obtenir més informació." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Corregeix", @@ -4705,7 +4739,7 @@ "message": "Error" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "L'administració d'usuaris també ha d'estar habilitada amb el permís de recuperació del compte de gestió" }, "setupProvider": { "message": "Configuració del proveïdor" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Només s'exportaran els elements personals de la caixa forta associats a $ORGANIZATION$. Els elements de la caixa forta personal no s'inclouran.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Exigeix que els membres existents canvien les seues contrasenyes" }, - "region": { - "message": "Regió" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "UE", - "description": "European Union" - }, - "us": { - "message": "EUA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "No teniu permisos per suprimir aquest projecte", @@ -6825,7 +6854,7 @@ "message": "Actualitza la configuració de KDF" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Dispositius de confiança" }, "memberDecryptionTdeDescriptionPartOne": { "message": "Una vegada autenticats, els membres desxifraran les dades de la caixa forta mitjançant una clau emmagatzemada al seu dispositiu. La", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Comptes de serveis addicionals" }, - "additionalServiceAccountsDesc": { - "message": "El vostre pla inclou $COUNT$ comptes de servei. Podeu afegir comptes de servei addicionals per $COST$ al mes.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Cost potencial màxim del compte de servei" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 6a19df3f836..7c9c0968353 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Vyskytla se neočekávaná chyba." }, + "expirationDateError": { + "message": "Zvolte datum vypršení platnosti, které bude v budoucnu." + }, "emailAddress": { "message": "E-mailová adresa" }, "yourVaultIsLocked": { "message": "Váš trezor je uzamčen. Pro pokračování musíte zadat hlavní heslo." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Odemknout" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopírovat ověřovací kód" }, + "copyUuid": { + "message": "Kopírovat UUID" + }, "warning": { "message": "Varování" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Chyba při dešifrování exportovaného souboru. Váš šifrovací klíč se neshoduje s klíčem použitým během exportu dat." }, + "importDestination": { + "message": "Cíl importu" + }, + "learnAboutImportOptions": { + "message": "Více o volbách importu" + }, + "selectImportFolder": { + "message": "Zvolte složku" + }, + "selectImportCollection": { + "message": "Zvolte kolekci" + }, + "importTargetHint": { + "message": "Pokud chcete obsah importovaného souboru přesunout do složky $DESTINATION$, vyberte tuto volbu", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Soubor obsahuje nepřiřazené položky." + }, "selectFormat": { "message": "Vyberte formát importovaného souboru" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Tato položka obsahuje staré přílohy, které vyžadují opravu." }, - "attachmentFixDesc": { - "message": "Tato stará příloha vyžaduje opravu. Klepněte pro více informací." + "attachmentFixDescription": { + "message": "Tato příloha používá zastaralé šifrování. Zvolte \"Opravit\" pro stažení, přešifrování a opětovné nahrání přílohy." }, "fix": { "message": "Opravit", @@ -5429,7 +5463,7 @@ } } }, - "exportingOrganizationVaultDescription": { + "exportingOrganizationVaultDesc": { "message": "Exportován bude jen trezor organizace přidružený k položce $ORGANIZATION$. Osobní položky trezoru a položky z jiných organizací nebudou zahrnuty.", "placeholders": { "organization": { @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Bude vyžadovat od stávajících členů, aby změnili svá hesla" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Nemáte oprávnění tento projekt smazat", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Další účty služby" }, - "additionalServiceAccountsDesc": { - "message": "Vaše předplatné obsahuje $COUNT$ účtů služby. Další účty služby můžete přidat za $COST$ měsíčně.", + "includedServiceAccounts": { + "message": "Váš plán již obsahuje $COUNT$ účtů služeb.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Za $COST$ měsíčně můžete přidat další účty služeb.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max. potenciální náklady na účet služby" + }, + "smBetaEndedDesc": { + "message": "Správce klíčů beta skončil $BETA_ENDING_DATE$. Zbývá $DAYS$ dní na to, abyste si přidali aplikaci správce klíčů k placenému předplatnému a zachovali si přístup k datům správce klíčů. Kontaktujte oddělení Customer Success a přidejte si aplikaci správce klíčů do svého předplatného.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta končí" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index fd395c891b3..6e742e4ad8b 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "En uventet fejl opstod." }, + "expirationDateError": { + "message": "Udløbsdato skal være i fremtiden." + }, "emailAddress": { "message": "E-mailadresse" }, "yourVaultIsLocked": { "message": "Din boks er låst. Bekræft din hovedadgangskode for at fortsætte." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Oplås" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopiér bekræftelseskoden" }, + "copyUuid": { + "message": "Kopiér UUID" + }, "warning": { "message": "Advarsel" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Fejl under dekryptering af den eksporterede fil. Krypteringsnøglen matcher ikke den til dataeksporten anvendte krypteringsnøgle." }, + "importDestination": { + "message": "Importér destination" + }, + "learnAboutImportOptions": { + "message": "Læs om importmuligheder" + }, + "selectImportFolder": { + "message": "Vælg en mappe" + }, + "selectImportCollection": { + "message": "Vælg en samling" + }, + "importTargetHint": { + "message": "Vælg denne indstilling, hvis importeret filindhold skal flyttet til en $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Filen indeholder ikke-tildelte emner." + }, "selectFormat": { "message": "Vælg formatet for importfilen" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Dette element har gamle filvedhæftninger, der skal repareres." }, - "attachmentFixDesc": { - "message": "Dette er en gammel filvedhæftning, der skal repareres. Klik for at lære mere." + "attachmentFixDescription": { + "message": "Denne vedhæftning har forældet kryptering. Vælg 'Korrigér' for at downloade, gen-kryptere og gen-uploade vedhæftningen." }, "fix": { "message": "Reparér", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Kun organisationsboksen tilknyttet $ORGANIZATION$ eksporteres. Individuelle boksemner, samt emner fra andre organisationer, medtages ikke.", + "exportingOrganizationVaultDesc": { + "message": "Kun organisationsboksen tilknyttet $ORGANIZATION$ eksporteres. Emner i individuelle bokse eller andre organisationer medtages ikke.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Kræv, at eksisterende medlemmer ændrer deres adgangskoder" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Ingen tilladelse til at slette dette objekt", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Yderligere tjenestekonti" }, - "additionalServiceAccountsDesc": { - "message": "Abonnementstypen inkluderer $COUNT$ tjenestekonti. Yderligere tjenestekonti kan tilføjes for $COST$ pr. måned.", + "includedServiceAccounts": { + "message": "Abonnementstypen er inkl. $COUNT$ tjenestekonti.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Yderligere tjenestekonti kan tilføjes for $COST$ pr. måned.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Maks. potentiel tjenestekontomkostning" + }, + "smBetaEndedDesc": { + "message": "Secrets Manager Beta ophørte $BETA_ENDING_DATE$. Der er $DAYS$ dage tilbage til at føje Secrets Manager til dit betalte abonnement og bevare adgangen til Secrets Manager-data. Kontakt Customer Success for at føje Secrets Manager til abonnementet.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta ophører" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 81ca7797b86..8f04f01be41 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ein unerwarteter Fehler ist aufgetreten." }, + "expirationDateError": { + "message": "Bitte wähle ein Ablaufdatum aus, das in der Zukunft liegt." + }, "emailAddress": { "message": "E-Mail-Adresse" }, "yourVaultIsLocked": { "message": "Dein Tresor ist gesperrt. Überprüfe dein Master-Passwort, um fortzufahren." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Entsperren" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Verifizierungscode kopieren" }, + "copyUuid": { + "message": "UUID kopieren" + }, "warning": { "message": "Warnung" }, @@ -1155,7 +1164,7 @@ } }, "kdfIterationsWarning": { - "message": "Wenn du die Anzahl der KDF-Iterationen zu hoch setzt, kann es sein, dass das Einloggen in (und Entsperren von) Bitwarden auf langsameren Geräten länger dauert. Wir empfehlen, dass du den Wert wiederholt um $INCREMENT$ anhebst und auf all deinen Geräten testest.", + "message": "Wenn du die Anzahl der KDF-Iterationen zu hoch setzt, kann es sein, dass das Anmelden in (und Entsperren von) Bitwarden auf langsameren Geräten länger dauert. Wir empfehlen, dass du den Wert wiederholt um $INCREMENT$ anhebst und auf all deinen Geräten testest.", "placeholders": { "increment": { "content": "$1", @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Fehler beim Entschlüsseln der exportierten Datei. Dein Verschlüsselungsschlüssel stimmt nicht mit dem beim Export verwendeten Verschlüsselungsschlüssel überein." }, + "importDestination": { + "message": "Import-Ziel" + }, + "learnAboutImportOptions": { + "message": "Erfahre mehr über deine Importoptionen" + }, + "selectImportFolder": { + "message": "Ordner auswählen" + }, + "selectImportCollection": { + "message": "Sammlung auswählen" + }, + "importTargetHint": { + "message": "Wähle diese Option, wenn der importierte Dateiinhalt in eine(n) $DESTINATION$ verschoben werden soll", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Die Datei enthält nicht zugewiesene Einträge." + }, "selectFormat": { "message": "Wählen Sie das Format Ihrer Import-Datei" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Dieser Eintrag hat Anhänge, die repariert werden müssen." }, - "attachmentFixDesc": { - "message": "Dieser Dateianhang muss aufgrund ihres Alters aktualisiert werden. Klicke hier, um mehr zu erfahren." + "attachmentFixDescription": { + "message": "Dieser Anhang verwendet eine veraltete Verschlüsselung. Wähle \"Reparieren\", um den Anhang herunterzuladen, erneut zu verschlüsseln und erneut hochzuladen." }, "fix": { "message": "Reparieren", @@ -4157,7 +4191,7 @@ "message": "Benutzer eingeladen." }, "acceptEmergencyAccess": { - "message": "Du wurdest eingeladen, ein Notfallkontakt für den oben genannten Benutzer zu werden. Um die Einladung anzunehmen, musst du dich einloggen oder ein neues Bitwarden-Konto erstellen." + "message": "Du wurdest eingeladen, ein Notfallkontakt für den oben genannten Benutzer zu werden. Um die Einladung anzunehmen, musst du dich anmelden oder ein neues Bitwarden-Konto erstellen." }, "emergencyInviteAcceptFailed": { "message": "Die Einladung konnte nicht angenommen werden. Bitte den Benutzer, eine neue Einladung zu versenden." @@ -4570,7 +4604,7 @@ "message": "Passwort zurücksetzen" }, "resetPasswordLoggedOutWarning": { - "message": "Wenn du fortfährst, wird $NAME$ aus seiner aktuellen Sitzung ausgeloggt und muss sich erneut einloggen. Aktive Sitzungen auf anderen Geräten können bis zu einer Stunde weiterhin aktiv bleiben.", + "message": "Wenn du fortfährst, wird $NAME$ aus seiner aktuellen Sitzung abgemeldet und muss sich erneut anmelden. Aktive Sitzungen auf anderen Geräten können bis zu einer Stunde weiterhin aktiv bleiben.", "placeholders": { "name": { "content": "$1", @@ -5094,7 +5128,7 @@ "message": "Angebot für eine bestehende Organisation akzeptieren oder eine neue Families-Organisation erstellen." }, "setupSponsoredFamiliesLoginDesc": { - "message": "Dir wurde ein kostenloser Bitwarden Families-Organisationstarif angeboten. Um fortzufahren, musst du dich in das Konto einloggen, das das Angebot erhalten hat." + "message": "Dir wurde ein kostenloser Bitwarden Families-Organisationstarif angeboten. Um fortzufahren, musst du dich bei dem Konto anmelden, das das Angebot erhalten hat." }, "sponsoredFamiliesAcceptFailed": { "message": "Angebot kann nicht angenommen werden. Bitte sende die Angebotsmail von deinem Unternehmenskonto erneut und versuche es noch einmal." @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Nur der mit $ORGANIZATION$ verbundene Tresor der Organisation wird exportiert. Persönliche Tresoreinträge und Einträge anderer Organisationen werden nicht berücksichtigt.", + "exportingOrganizationVaultDesc": { + "message": "Nur der mit $ORGANIZATION$ verbundene Organisationstresor wird exportiert. Einträge in persönlichen Tresoren oder anderen Organisationen werden nicht berücksichtigt.", "placeholders": { "organization": { "content": "$1", @@ -5589,7 +5623,7 @@ "message": "Geräteverifizierung aktivieren" }, "deviceVerificationDesc": { - "message": "Verifizierungscodes werden an deine E-Mail-Adresse gesendet, wenn du dich von einem unbekannten Gerät einloggst" + "message": "Verifizierungscodes werden an deine E-Mail-Adresse gesendet, wenn du dich von einem unbekannten Gerät anmeldest" }, "updatedDeviceVerification": { "message": "Geräteverifizierung aktualisiert" @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Bestehende Mitglieder auffordern, ihre Passwörter zu ändern" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Dir fehlen die Berechtigungen, dieses Projekt zu löschen", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Zusätzliche Dienstkonten" }, - "additionalServiceAccountsDesc": { - "message": "Dein Abonnement enthält $COUNT$ Dienstkonten. Du kannst zusätzliche Dienstkonten für $COST$ pro Monat hinzufügen.", + "includedServiceAccounts": { + "message": "Dein Tarif enthält $COUNT$ Dienstkonten.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Du kannst zusätzliche Dienstkonten für $COST$ pro Monat hinzufügen.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Maximale potentielle Kosten für Dienstkonten" + }, + "smBetaEndedDesc": { + "message": "Die Secrets Manager Beta endete am $BETA_ENDING_DATE$. Du hast $DAYS$ Tage Zeit, um den Secrets Manager zu deinem kostenpflichtigen Abonnement hinzuzufügen und den Zugriff auf die Daten des Secrets Manager zu erhalten. Kontaktiere den Kundensupport, um den Secrets Manager zu deinem Abonnement hinzuzufügen.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta-Ende" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index f4ecd15a4fb..75f8d10f14c 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Παρουσιάστηκε ένα μη αναμενόμενο σφάλμα." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Διεύθυνση Email" }, "yourVaultIsLocked": { "message": "Το vault σας είναι κλειδωμένο. Επαληθεύστε τον κύριο κωδικό πρόσβασης για να συνεχίσετε." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Ξεκλείδωμα" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Αντιγραφή Κωδικού Επαλήθευσης" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Προειδοποίηση" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Σφάλμα αποκρυπτογράφησης του εξαγόμενου αρχείου. Το κλειδί κρυπτογράφησης δεν ταιριάζει με το κλειδί κρυπτογράφησης που χρησιμοποιήθηκε για την εξαγωγή των δεδομένων." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Επιλέξτε μορφή του αρχείου εισαγωγής" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Αυτό το στοιχείο έχει παλιά συνημμένα αρχεία που πρέπει να διορθωθούν." }, - "attachmentFixDesc": { - "message": "Αυτό είναι ένα παλιό συνημμένο αρχείο που πρέπει να διορθωθεί. Κάντε κλικ για να μάθετε περισσότερα." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Επιδιόρθωση", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Μόνο το vault οργανισμού που σχετίζεται με το $ORGANIZATION$ θα εξαχθεί. Προσωπικά αντικείμενα και αντικείμενα από άλλους οργανισμούς δεν θα συμπεριληφθούν.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index febe1163884..ca217f6ef77 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organisation vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organisations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index fcefb38e2ca..ac8fa6dd682 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 6e0ad0c4199..6f955af6b54 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Neatendita eraro okazis." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Retpoŝta Adreso" }, "yourVaultIsLocked": { "message": "Via trezorejo estas ŝlosita. Kontrolu vian ĉefan pasvorton por daŭrigi." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Malŝlosi" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopii Konfirman Kodon" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Averto" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Eraro de ĉifrado de la elporta dosiero. Via ŝlosilo de ĉifrado ne kongruas kun la ŝlosilo de ĉifrado uzita por elporti la datumojn." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Elektu la formaton de la importa dosiero" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Ĉi tiu ero havas malnovajn dosierajn aldonaĵojn, kiujn necesas ripari." }, - "attachmentFixDesc": { - "message": "Ĉi tio estas malnova riparenda dosiero, kiun necesas ripari. Alklaku por lerni pli." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Ripari", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index c4e6c9ec89b..b9cdefb5b89 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ha ocurrido un error inesperado." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Correo electrónico" }, "yourVaultIsLocked": { "message": "Tu caja fuerte está bloqueada. Verifica tu contraseña maestra para continuar." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Desbloquear" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copiar código de verificación" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Advertencia" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error al descifrar el archivo exportado. Su clave de cifrado no coincide con la clave de cifrado utilizada para exporta los datos." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Selecciona el formato del fichero a importar" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Este elemento tiene archivos adjuntos antiguos que deben ser corregidos." }, - "attachmentFixDesc": { - "message": "Este es un archivo adjunto antiguo que necesita ser corregido. Haga clic para obtener más información." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Arreglar", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Solo se exportará la caja fuerte de la organización asociada con $ORGANIZATION$. No se incluirán objetos y elementos personales de otras organizaciones.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Requiere que los miembros existentes cambien sus contraseñas" }, - "region": { - "message": "Región" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "EEUU", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "No tiene permisos para eliminar este proyecto", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 2dcc6e2927a..e6646326dcd 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Tekkis ootamatu viga." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-posti aadress" }, "yourVaultIsLocked": { "message": "Hoidla on lukus. Jätkamiseks sisesta ülemparool." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Lukusta lahti" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopeeri kinnituskood" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Hoiatus" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Eksporditud faili dekrüpteerimine nurjus. Sinu krüpteerimisvõti ei ühti selle võtmega, mida kasutati andmete eksportimisel." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Vali imporditava faili vorming" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Sellel kirjel on vanu manuseid, mille peab parandama." }, - "attachmentFixDesc": { - "message": "See on vana failimanus, mille peab parandama. Rohkema teabe saamiseks kliki siia." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Paranda", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Ainult organisatsiooniga $ORGANIZATION$ seotud kirjed eksportidakse. Personaalse hoidla ja teiste organisatsioonide kirjeid ei ekspordita.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 57997785d9d..75e378ecda6 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ustekabeko akatsa gertatu da." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Emaila" }, "yourVaultIsLocked": { "message": "Zure kutxa gotorra blokeatuta dago. Egiaztatu zure pasahitz nagusia jarraitzeko." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Desblokeatu" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopiatu egiaztatze-kodea" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Kontuz" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Errorea esportatutako fitxategia deszifratzean. Zifratze-gakoa ez dator bat datuak esportatzeko erabilitako zifratze-gakoarekin." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Hautatu inportazio fitxategiaren formatua" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Artikulu honek fitxategi erantsi zaharrak ditu, konpondu beharrekoak." }, - "attachmentFixDesc": { - "message": "Hau fitxategi erantsi zaharra da eta konpondu beharra dago. Klikatu gehiago jakiteko." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Konpondu", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "$ORGANIZATION$-rekin lotutako erakundearen kutxa gotorra bakarrik esportatuko da. Ez dira sartuko kutxa gotor pertsonaletako eta beste erakunde batzuetako elementuak.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index ff293244d5f..08246e95942 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "یک خطای غیر منتظره رخ داده است." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "نشانی ایمیل" }, "yourVaultIsLocked": { "message": "گاوصندوق شما قفل است. برای ادامه کلمه عبور اصلی خود را وارد کنید." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "باز کردن قفل" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "کپی کد تأیید" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "هشدار" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "خطا در رمزگشایی پرونده‌ی درون ریزی شده. کلید رمزگذاری شما با کلید رمزگذاری استفاده شده برای درون ریزی داده‌ها مطابقت ندارد." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "فرمت پرونده‌ی درون ریزی را انتخاب کنید" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "این مورد دارای پرونده های پیوست قدیمی است که باید اصلاح شوند." }, - "attachmentFixDesc": { - "message": "این یک پرونده پیوست قدیمی است که باید اصلاح شود. برای کسب اطلاعات بیشتر کلیک کنید." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "اصلاح", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "فقط گاوصندوق سازمانی مرتبط با $ORGANIZATION$ برون ریزی خواهد شد. اقلام شخصی گاوصندوق و اقلام سایر سازمان‌ها شامل نمی‌شود.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "از اعضای موجود بخواهید کلمه‌های عبور خود را تغییر دهند" }, - "region": { - "message": "منطقه" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "اروپا", - "description": "European Union" - }, - "us": { - "message": "امریکا", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "شما مجوز حذف این پروژه را ندارید", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "حساب‌های خدمات اضافی" }, - "additionalServiceAccountsDesc": { - "message": "طرح شما با حساب خدمات $COUNT$ همراه است. می‌توانید حساب‌های سرویس اضافی را با $COST$ در ماه اضافه کنید.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "حداکثر هزینه حساب خدمات بالقوه" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 51b2af3213b..f589399377b 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Tapahtui odottamaton virhe." }, + "expirationDateError": { + "message": "Valitse erääntymispäivä, joka on tulevaisuudessa." + }, "emailAddress": { "message": "Sähköpostiosoite" }, "yourVaultIsLocked": { "message": "Holvi on lukittu. Jatka vahvistamalla pääsalasanasi." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Avaa holvi" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopioi todennuskoodi" }, + "copyUuid": { + "message": "Kopioi UUID" + }, "warning": { "message": "Varoitus" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Virhe purettaessa viedyn tiedoston salausta. Salausavaimesi ei vastaa viennissä käytettyä salausavainta." }, + "importDestination": { + "message": "Tuontikohde" + }, + "learnAboutImportOptions": { + "message": "Lue lisää tuontivaihtoehdoista" + }, + "selectImportFolder": { + "message": "Valitse kansio" + }, + "selectImportCollection": { + "message": "Valitse kokoelma" + }, + "importTargetHint": { + "message": "Valitse tämä, jos haluat tuoda tiedoston sisällön kohteesee $DESTINATION$.", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Tiedosto sisältää määrittämättömiä kohteita." + }, "selectFormat": { "message": "Valitse tuotavan tiedoston muoto" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Kohteella on vanhoja tiedostoliitteitä, jotka on korjattava." }, - "attachmentFixDesc": { - "message": "Vanha tiedostoliite, joka on korjattava. Klikkaa lukeaksesi lisää." + "attachmentFixDescription": { + "message": "Liite käyttää vanhentunutta salausta. Lataa liite, salaa se uudelleen ja lisää se uudestaan valitsemalla \"Korjaa\"." }, "fix": { "message": "Korjaa", @@ -4705,7 +4739,7 @@ "message": "Virhe" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "\"Tilien palautusavun hallinta\" -oikeuden kanssa on myönnettävä myös \"Käyttäjien hallinta\" -oikeus." }, "setupProvider": { "message": "Toimittajan määritys" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Vain organisaatioon $ORGANIZATION$ liitetyt holvin kohteet viedään. Yksityisen holvin ja muiden organisaatioiden kohteita ei sisällytetä.", + "exportingOrganizationVaultDesc": { + "message": "Vain organisaatioon $ORGANIZATION$ liitetyn holvin kohteet viedään. Yksityisen holvin ja muiden organisaatioiden kohteita ei sisällytetä.", "placeholders": { "organization": { "content": "$1", @@ -6512,7 +6546,7 @@ "message": "Organisaation salaisten tietojen vienti" }, "exportingOrganizationSecretDataDescription": { - "message": "Vain organisaatioon $ORGANIZATION$ liitetyt Salaisuushallinnan tiedot viedään. Muiden tuotteiden ja organisaatioiden kohteet eivät sisälly tähän.", + "message": "Vain organisaatioon $ORGANIZATION$ liitetyt Salaisuushallinnan tiedot viedään. Muiden tuotteiden tai organisaatioiden kohteita ei sisällytetä.", "placeholders": { "ORGANIZATION": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Vaadi nykyisiä jäseniä vaihtamaan salasanansa" }, - "region": { - "message": "Alue" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Käyttöoikeutesi eivät salli tämän projektin poistamista", @@ -6825,7 +6854,7 @@ "message": "Päivitä KDF-asetukset" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Luotetut laitteet" }, "memberDecryptionTdeDescriptionPartOne": { "message": "Kun jäsenet on todennettu, he voivat purkaa holvin salauksen heidän laitteellaan säilytettävällä avaimella.", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Lisättävät palvelutilit" }, - "additionalServiceAccountsDesc": { - "message": "Tilaukseesi sisältyy $COUNT$ palvelutiliä ja voit hankkia lisää hintaan $COST$/palvelutili/kuukausi.", + "includedServiceAccounts": { + "message": "Tilaukseesi sisältyy $COUNT$ palvelutiliä.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Voit hankkia lisää palvelutilejä hintaan $COST$/tili/kuukausi.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Palvelutilin mahdollinen enimmäiskustannus" + }, + "smBetaEndedDesc": { + "message": "Salaisuushallinnan beta päättyi $BETA_ENDING_DATE$. Sinulla on $DAYS$ päivää aikaa lisätä Salaisuushallinta maksulliseen tilaukseesi säilyttääksesi Salaisuushallinnassa olevien tietojesi käyttöoikeuden. Lisää Salaisuushallinta tilaukseesi olemalla yhteydessä asiakaspalveluun.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta päättyy" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index a8e52862306..d7260c3d45b 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Nagkaroon ng hindi inaasahang problema." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Naka-lock ang vault mo. Beripikahin ang master password mo para tumuloy." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "I-unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopyahin ang code pamberipika" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Babala" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Nagkaproblema sa pagde-decrypt ng na-export na file. Hindi tumutugma ang encryption key mo sa ginamit pang-export sa data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Piliin ang format ng import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Ang item na ito ay may mga lumang file attachment na kailangang ayusin." }, - "attachmentFixDesc": { - "message": "Ito ay isang lumang file attachment ang mga pangangailangan upang ayusin. Mag-klik para malaman ang iba pa." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Ayusin ang", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Tanging ang vault ng organisasyon na nauugnay sa $ORGANIZATION$ ang i-export. Hindi isasama ang mga indibidwal na vault item at item mula sa ibang mga organisasyon.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 235180cc3c8..8796f4d9b45 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Une erreur inattendue est survenue." }, + "expirationDateError": { + "message": "Veuillez sélectionner une date d'expiration qui est dans le futur." + }, "emailAddress": { "message": "Adresse électronique" }, "yourVaultIsLocked": { "message": "Votre coffre est verrouillé. Vérifiez votre mot de passe principal pour continuer." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Déverrouiller" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copier le code de vérification" }, + "copyUuid": { + "message": "Copier l'UUID" + }, "warning": { "message": "Attention" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Erreur lors du déchiffrement du fichier exporté. Votre clé de chiffrement ne correspond pas à la clé de chiffrement utilisée pour exporter les données." }, + "importDestination": { + "message": "Destination de l'import" + }, + "learnAboutImportOptions": { + "message": "En savoir plus sur vos options d'importation" + }, + "selectImportFolder": { + "message": "Choisir un dossier" + }, + "selectImportCollection": { + "message": "Sélectionnez une collection" + }, + "importTargetHint": { + "message": "Sélectionnez cette option si vous voulez que le contenu du fichier importé soit déplacé vers un(e) $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Le fichier contient des éléments non assignés." + }, "selectFormat": { "message": "Sélectionnez le format du fichier à importer" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Cet élément a d'anciennes pièces jointes qui doivent être réparées." }, - "attachmentFixDesc": { - "message": "Il s'agit d'une ancienne pièce jointe qui doit être réparée. Cliquez pour en savoir plus." + "attachmentFixDescription": { + "message": "Cette pièce jointe utilise un chiffrement obsolète. Choisissez 'Réparer' pour télécharger, chiffrer à nouveau et téléverser à nouveau la pièce jointe." }, "fix": { "message": "Réparer", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Seul le coffre de l'organisation associé à $ORGANIZATION$ sera exporté. Les éléments du coffre personnel et les éléments d'autres organisations ne seront pas inclus.", + "exportingOrganizationVaultDesc": { + "message": "Seul le coffre d'organisation associé à $ORGANIZATION$ sera exporté. Les éléments dans les coffres individuels ou d'autres organisations ne seront pas inclus.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Exiger que les membres existants changent leurs mots de passe" }, - "region": { - "message": "Région" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Vous n'avez pas les droits pour supprimer ce projet", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Comptes de service supplémentaires" }, - "additionalServiceAccountsDesc": { - "message": "Votre plan inclut $COUNT$ comptes de service. Vous pouvez ajouter des comptes de service supplémentaires pour $COST$ par mois.", + "includedServiceAccounts": { + "message": "Votre forfait inclut $COUNT$ comptes de service.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Vous pouvez ajouter des comptes de service supplémentaires pour $COST$ par mois.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Coût potentiel maximum du compte de service" + }, + "smBetaEndedDesc": { + "message": "Le Secrets Manager Beta s'est terminé le $BETA_ENDING_DATE$. Il vous reste $DAYS$ jours pour ajouter Secrets Manager à votre forfait payant et conserver votre accès à vos données de Secrets Manager. Contactez Customer Success pour ajouter Secrets Manager à votre forfait.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Fin de la beta" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 2bf2db8832a..afb6d2214cc 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "אירעה שגיאה לא צפויה." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "כתובת אימייל" }, "yourVaultIsLocked": { "message": "הכספת שלך נעולה. הזן את הסיסמה הראשית שלך כדי להמשיך." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "שחרר נעילה" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "העתק קוד אימות" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "אזהרה" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "בחר את פורמט הקובץ לייבוא" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "לפריט זה יש קובץ מצורף שצריך תיקון." }, - "attachmentFixDesc": { - "message": "קובץ מצורף זה צריך לעבור תיקון. לחץ כאן כדי לגלות עוד פרטים." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "תקן", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 7a015596ca1..51e2caf5319 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 32bb069c0d1..dc852c9f4e6 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Došlo je do neočekivane pogreške." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Adresa e-pošte" }, "yourVaultIsLocked": { "message": "Tvoj trezor je zaključan. Potvrdi glavnu lozinku za nastavak." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Otključaj" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopiraj kôd za provjeru" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Upozorenje" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Greška u dešifriranju izvozne datoteke. Ovaj ključ za šifriranje ne odgovara ključu za šifriranje korištenom pri izvozu datoteke." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Odaberi format datoteke za uvoz" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Ova stavka ima stare privitke koje je potrebno popraviti." }, - "attachmentFixDesc": { - "message": "Ovo je stari privitak kojeg je potrebno popraviti. Klikni ovdje za više informacija." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Popravi", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Izvest će se samo organizacijski trezor povezan s $ORGANIZATION$. Pojedinačne stavke iz trezora i stavke iz drugih organizacija neće biti uključene.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 29b614cf641..cd764ebd422 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Váratlan hiba történt." }, + "expirationDateError": { + "message": "Válasszunk jövőbeni lejárati dátumot." + }, "emailAddress": { "message": "Email cím" }, "yourVaultIsLocked": { "message": "A széf zárolásra került. A folytatáshoz meg kell adni a mesterjelszót." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Feloldás" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy Verification Code" }, + "copyUuid": { + "message": "UUID másolása" + }, "warning": { "message": "Figyelmeztetés" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Hiba történt az exportált fájl visszafejtése során. A titkosítási kulcs nem egyezik meg az adatok exportálásához használt titkosítási kulccsal." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Válasszuk ki az import fájl formátumát." }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Ennek az elemnek régi fájl mellékletei vannak, amelyeket javítani kell." }, - "attachmentFixDesc": { - "message": "Ez egy régi melléklet, amelyet javítani kell. Kattintás több információért." + "attachmentFixDescription": { + "message": "Ez a melléklet elavult titkosítást használ. Válasszuk a 'Javítás' lehetőséget a melléklet letöltéséhez, ismételt titkosításához és újbóli feltöltéséhez." }, "fix": { "message": "Javítás", @@ -4705,7 +4739,7 @@ "message": "Hiba" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "A felhasználók kezelését engedélyezni kell a Jelszó visszaállításának kezelése jogosultsággal is." }, "setupProvider": { "message": "Szolgáltató beállítása" @@ -5429,7 +5463,7 @@ } } }, - "exportingOrganizationVaultDescription": { + "exportingOrganizationVaultDesc": { "message": "Csak$ORGANIZATION$ névvel társított szervezeti széf elemek kerülnek exportálásra. Ebbe nem kerülnek be a személyes és más szervezeti széf elemek.", "placeholders": { "organization": { @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "A meglévő tagoknak meg kell változtatniuk jelszavaikat." }, - "region": { - "message": "Régió" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Nincs jogosulltság ezen projekt törléséhez.", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Kiegészítő szolgáltatás fiókok" }, - "additionalServiceAccountsDesc": { - "message": "A csomag $COUNT$ szolgáltatás fiókot tartalmaz. További szolgáltatási fiókokat vehetünk fel havi $COST$ áron.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Maximális lehetséges szolgáltatás fiókköltség" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 1923fadea94..eabcdc841f2 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Terjadi kesalahan yang tak diduga." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Alamat Surel" }, "yourVaultIsLocked": { "message": "Brankas Anda terkunci. Verifikasi kata sandi utama Anda untuk melanjutkan." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Buka Kunci" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Salin Kode Verifikasi" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Peringatan" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Tidak dapat mendekripsi file yang diekspor. Kunci enkripsi Anda tidak cocok dengan kunci enkripsi yang digunakan untuk menekspor data terkait." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Pilih format file impor" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Item ini memiliki lampiran file lama yang perlu diperbaiki." }, - "attachmentFixDesc": { - "message": "Ini adalah lampiran file lama yang perlu diperbaiki. Klik untuk mempelajari lebih lanjut." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Perbaiki", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index d31a4788712..752ca51b8e6 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Si è verificato un errore imprevisto." }, + "expirationDateError": { + "message": "Seleziona una data di scadenza nel futuro." + }, "emailAddress": { "message": "Indirizzo email" }, "yourVaultIsLocked": { "message": "La tua cassaforte è bloccata. Verifica la tua password principale per continuare." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Sblocca" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copia codice di verifica" }, + "copyUuid": { + "message": "Copia UUID" + }, "warning": { "message": "Attenzione" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Errore durante la decriptografia del file esportato. La chiave di criptografia non corrisponde alla chiave di criptografia usata per esportare i dati." }, + "importDestination": { + "message": "Destinazione dell'importazione" + }, + "learnAboutImportOptions": { + "message": "Ulteriori informazioni sulle tue opzioni di importazione" + }, + "selectImportFolder": { + "message": "Seleziona una cartella" + }, + "selectImportCollection": { + "message": "Seleziona una raccolta" + }, + "importTargetHint": { + "message": "Seleziona questa opzione se vuoi che i contenuti del file di importazione siamo spostati in una $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Il file contiene elementi non assegnati." + }, "selectFormat": { "message": "Seleziona il formato del file da importare" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Questo elemento ha vecchi file allegati che devono essere corretti." }, - "attachmentFixDesc": { - "message": "Questo è un vecchio file allegato che deve essere corretto. Clicca per ulteriori informazioni." + "attachmentFixDescription": { + "message": "Questo allegato usa una criptografia obsoleta. Seleziona \"Correggi\" per scaricare, criptografare di nuovo, e caricare di nuovo l'allegato." }, "fix": { "message": "Correggi", @@ -4705,7 +4739,7 @@ "message": "Errore" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "Gestisci utenti deve essere abilitato con il permesso di gestire il ripristino delle password" }, "setupProvider": { "message": "Configurazione del fornitore" @@ -5429,7 +5463,7 @@ } } }, - "exportingOrganizationVaultDescription": { + "exportingOrganizationVaultDesc": { "message": "Solo la cassaforte dell'organizzazione associata a $ORGANIZATION$ sarà esportata. Elementi nelle casseforti individuali o in altre organizzazioni non saranno inclusi.", "placeholders": { "organization": { @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Obbliga i membri esistenti a cambiare le loro password" }, - "region": { - "message": "Regione" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "UE", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Non hai l'autorizzazione per eliminare questo progetto", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Account di servizio aggiuntivi" }, - "additionalServiceAccountsDesc": { - "message": "Il tuo piano include $COUNT$ account di servizio. Puoi aggiungere più account di servizio a $COST$ al mese.", + "includedServiceAccounts": { + "message": "Il tuo piano include $COUNT$ account di servizio.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Puoi aggiungere più account di servizio a $COST$ al mese.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Costo massimo potenziale dell'account di servizio" + }, + "smBetaEndedDesc": { + "message": "La beta del Gestore dei Segreti è terminata in $BETA_ENDING_DATE$. Ti rimangono $DAYS$ giorni per aggiungere il Gestore dei Segreti al tuo abbonamento a pagamento e mantenere l'accesso ai dati del Gestore dei Segreti. Contatta il Successo del Cliente per aggiungere il Gestore dei Segreti al tuo abbonamento.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Termine della beta" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index ac23ddd5ab4..44be0b070f9 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "予期せぬエラーが発生しました。" }, + "expirationDateError": { + "message": "有効期限は未来のものをお選びください。" + }, "emailAddress": { "message": "メールアドレス" }, "yourVaultIsLocked": { "message": "保管庫がロックされています。開くにはマスターパスワードを入力してください。" }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "ロック解除" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "認証コードのコピー" }, + "copyUuid": { + "message": "UUID をコピー" + }, "warning": { "message": "注意" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "エクスポートされたファイルの復号でエラーが発生しました。暗号化キーが、データをエクスポートするために使用された暗号化キーと一致しません。" }, + "importDestination": { + "message": "インポート先" + }, + "learnAboutImportOptions": { + "message": "インポートオプションについて学ぶ" + }, + "selectImportFolder": { + "message": "フォルダーを選択" + }, + "selectImportCollection": { + "message": "コレクションを選択" + }, + "importTargetHint": { + "message": "インポートしたファイルコンテンツを $DESTINATION$ に移動したい場合は、このオプションを選択してください。", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "割り当てられていないアイテムがファイルに含まれています。" + }, "selectFormat": { "message": "インポートするファイルの形式を選択" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "このアイテムは添付ファイルが古いため修正する必要があります。" }, - "attachmentFixDesc": { - "message": "これは古い添付ファイルのため修正する必要があります。詳しくはこちら。" + "attachmentFixDescription": { + "message": "この添付ファイルは古い暗号化を使用しています。添付ファイルをダウンロード、再暗号化、再アップロードするには「修正」を選択してください。" }, "fix": { "message": "修正", @@ -4705,7 +4739,7 @@ "message": "エラー" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "ユーザーを管理するには、アカウントのリカバリ管理権限を付与する必要があります。" }, "setupProvider": { "message": "プロバイダーのセットアップ" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "$ORGANIZATION$ に関連付けられた組織保管庫のアイテムのみがエクスポートされます。他の組織や個人保管庫のアイテムは含まれません。", + "exportingOrganizationVaultDesc": { + "message": "$ORGANIZATION$ に関連付けられた組織保管庫のみがエクスポートされます。個々の保管庫または他の組織にあるアイテムは含まれません。", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "パスワードを変更するには、メンバーが存在しないといけません" }, - "region": { - "message": "リージョン" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "米国", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "このプロジェクトを削除する権限がありません", @@ -6825,7 +6854,7 @@ "message": "KDF の設定を更新する" }, "trustedDevices": { - "message": "Trusted devices" + "message": "信頼できるデバイス" }, "memberDecryptionTdeDescriptionPartOne": { "message": "認証が完了したあとメンバーはデバイスに保存されているキーを使用して保管庫のデータを復号します。このオプションを使用すると、", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "追加のサービスアカウント" }, - "additionalServiceAccountsDesc": { - "message": "あなたのプランには、 $COUNT$ 個のサービスアカウントが付属しています。月額 $COST$ でサービスアカウントを追加できます。", + "includedServiceAccounts": { + "message": "お客様のプランには$COUNT$のサービスアカウントが付属しています。", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "月々$COST$でサービスアカウントを追加できます。", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "潜在的なサービスアカウントのコストの最大値" + }, + "smBetaEndedDesc": { + "message": "シークレットマネージャーベータは$BETA_ENDING_DATE$で終了しました。シークレットマネージャーを有料サブスクリプションに追加し、シークレットマネージャーのデータへのアクセスを維持するには、$DAYS$日間の猶予があります。シークレットマネージャーをサブスクリプションに追加するには、カスタマーサクセスまでお問い合わせください。", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "ベータ終了" + }, + "beta": { + "message": "ベータ" } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 56488567c87..7929c24f01d 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "წარმოიშვა გაუთვალისწინებელი ხარვეზი." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "ელ-ფოსტის მისამართი" }, "yourVaultIsLocked": { "message": "თქვენი საცავი ჩაკეტილია. დაადასტურეთ თქვენი მთავარი პაროლი გასაგრძელებლად." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "გახსნა" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "დააკოპირე ერთჯერადი კოდი" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "გაფრთხილება" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 2ec0b9f2e40..49616196430 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "ಅನಿರೀಕ್ಷಿತ ದೋಷ ಸಂಭವಿಸಿದೆ." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "ಇಮೇಲ್ ವಿಳಾಸ" }, "yourVaultIsLocked": { "message": "ನಿಮ್ಮ ವಾಲ್ಟ್ ಲಾಕ್ ಆಗಿದೆ. ಮುಂದುವರೆಯಲು ನಿಮ್ಮ ಮಾಸ್ಟರ್ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "ಅನ್‌ಲಾಕ್ ಮಾಡಿ" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "ಪರಿಶೀಲನೆ ಕೋಡ್ ನಕಲಿಸಿ" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "ಎಚ್ಚರಿಕೆ" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "ರಫ್ತು ಮಾಡಿದ ಫೈಲ್ ಅನ್ನು ಡೀಕ್ರಿಪ್ಟ್ ಮಾಡುವಲ್ಲಿ ದೋಷ. ಡೇಟಾವನ್ನು ರಫ್ತು ಮಾಡಲು ಬಳಸಿದ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಗೆ ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "ಆಮದು ಫೈಲ್‌ನ ಸ್ವರೂಪವನ್ನು ಆಯ್ಕೆಮಾಡಿ" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "ಈ ಐಟಂ ಹಳೆಯ ಫೈಲ್ ಲಗತ್ತುಗಳನ್ನು ಹೊಂದಿದ್ದು ಅದನ್ನು ಸರಿಪಡಿಸಬೇಕಾಗಿದೆ." }, - "attachmentFixDesc": { - "message": "ಇದು ಸರಿಪಡಿಸಬೇಕಾದ ಹಳೆಯ ಫೈಲ್ ಲಗತ್ತು. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಕ್ಲಿಕ್ ಮಾಡಿ." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "ಹೊಂದಿಸು", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 14f68e767e8..a98c4a4d414 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "예기치 못한 오류가 발생했습니다." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "이메일 주소" }, "yourVaultIsLocked": { "message": "보관함이 잠겨 있습니다. 마스터 비밀번호를 입력하여 계속하세요." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "잠금 해제" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "인증 코드 복사" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "경고" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "내보내려는 파일을 복호화하던 중 오류가 발생했습니다. 암호화 키가 내보내려는 데이터를 암호화한 키와 일치하지 않습니다." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "불러올 파일의 포맷" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "이 항목은 수정이 필요한 오래된 파일을 갖고 있습니다." }, - "attachmentFixDesc": { - "message": "이것은 수정이 필요한 오래된 파일입니다. 자세한 내용을 보려면 클릭하십시오." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "수정", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index c6975de668c..89954567910 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ir radusies neparedzēta kļūda." }, + "expirationDateError": { + "message": "Lūgums atlasīt beigu datumu, kas ir nākotnē." + }, "emailAddress": { "message": "E-pasta adrese" }, "yourVaultIsLocked": { "message": "Tava glabātuve ir bloķēta. Lai turpinātu, pārbaudi savu galveno paroli." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Atslēgt" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Ievietot apstiprinājuma kodu starpliktuvē" }, + "copyUuid": { + "message": "Ievietot UUID starpliktuvē" + }, "warning": { "message": "Brīdinājums" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Atšifrējot eksportēto failu, radās kļūda. Jūsu šifrēšanas atslēga neatbilst datu eksportēšanai izmantotajai šifrēšanas atslēgai." }, + "importDestination": { + "message": "Ievietošanas galamērķis" + }, + "learnAboutImportOptions": { + "message": "Uzzināt par ievietošanas iespējām" + }, + "selectImportFolder": { + "message": "Atlasīt mapi" + }, + "selectImportCollection": { + "message": "Atlasīt krājumu" + }, + "importTargetHint": { + "message": "Šī iespēja jāatlasa, ja ir vēlēšanās ievietotās datnes saturu pārvietot uz $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Datne satur nepiešķirtus vienumus." + }, "selectFormat": { "message": "Atlasīt ievietošanas datnes veidolu" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Šim vienumam ir veci datņu pielikumi, kas ir jāsalabo." }, - "attachmentFixDesc": { - "message": "Šis ir vecs datnes pielikums, kas ir jāsalabo. Klikšķināt, lai uzzinātu vairāk." + "attachmentFixDescription": { + "message": "Šis pielikumam ir novecojusi šifrēšana. Jāatlasa 'Salabot', lai lejupielādētu, atkārtoti šifrētu un augšupielādētu pielikumu." }, "fix": { "message": "Salabot", @@ -4705,7 +4739,7 @@ "message": "Kļūda" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "Lietotāju pārvaldīšanai ir jābūt iespējotai arī ar konta atkopšanas pārvaldīšanas atļauju" }, "setupProvider": { "message": "Sniedzēja iestatīšana" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Tiks izdota tikai apvienības glabātava, kas ir saistīta ar $ORGANIZATION$. Personīgie glabātavas vienumi un vienumi no citām apvienībām netiks iekļauti.", + "exportingOrganizationVaultDesc": { + "message": "Tiks izdota tikai apvienības glabātava, kas ir saistīta ar $ORGANIZATION$. Atsevišķu glabātavu vai citu apvienību vienumi netiks iekļauti.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Pieprasīt esošajiem dalībniekiem nomainīt to paroles" }, - "region": { - "message": "Apgabals" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "ES", - "description": "European Union" - }, - "us": { - "message": "ASV", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Nav nepieciešamo atļauju, lai izdzēstu šo projektu", @@ -6825,7 +6854,7 @@ "message": "Atjaunināt KDF iestatījumus" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Uzticamās ierīces" }, "memberDecryptionTdeDescriptionPartOne": { "message": "Pēc pieteikšanās dalībnieki atšifrēs glabātavas saturu ar ierīcē glabātu atslēgu. ", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Papildu pakalpojumu konti" }, - "additionalServiceAccountsDesc": { - "message": "Plānā ir iekļauti $COUNT$ pakalpojumu konti. Papildu pakalpojumu kontus var pievienot par $COST$ mēnesī.", + "includedServiceAccounts": { + "message": "Plānā ir iekļauti $COUNT$ pakalpojumu konti.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Papildu pakalpojumu kontus var pievienot par $COST$ mēnesī.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Lielākās iespējamās pakalpojumu kontu izmaksas" + }, + "smBetaEndedDesc": { + "message": "Noslēpumu pārvaldnieka beta beidzās $BETA_ENDING_DATE$. Ir atlikušas $DAYS$ dienas, lai pievienotu Noslēpumu pārvaldnieku apmaksātajam abonementam un saglabātu piekļuvu Noslēpumu pārvaldnieka datiem. Jāsazināš ar klientu atbalstu, lai abonementam pievienotu Noslēpumu pārvaldnieku.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta ir beidzies" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 7896fe2b503..01def969c7d 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "ഒരു അപ്രതീക്ഷിത പിശക് സംഭവിച്ചു." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "ഇ-മെയിൽ വിലാസം" }, "yourVaultIsLocked": { "message": "നിങ്ങളുടെ വാൾട് പൂട്ടിയിരിക്കുന്നു. തുടരുന്നതിന് നിങ്ങളുടെ പ്രാഥമിക പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "അൺലോക്ക്" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "സ്ഥിരീകരണ കോഡ് പകർത്തുക " }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "മുന്നറിയിപ്പ്" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "പരിഹരിക്കേണ്ട അറ്റാച്മെന്റുകൾ ഈ ഇനത്തിൽ ഉണ്ട്." }, - "attachmentFixDesc": { - "message": "പരിഹരിക്കേണ്ട അറ്റാച്മെന്റുകൾ ഈ ഇനത്തിൽ ഉണ്ട്. കൂടുതലറിയാൻ ക്ലിക്കുചെയ്യുക." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "പരിഹരിക്കുക", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 1d707362bfc..580fdbd0d41 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "En uventet feil har oppstått." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-postadresse" }, "yourVaultIsLocked": { "message": "Hvelvet ditt er låst. Kontroller hovedpassordet ditt for å fortsette." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Lås opp" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopier verifiseringskoden" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Advarsel" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Feil under dekryptering av den eksporterte filen. Krypteringsnøkkelen samsvarte ikke med krypteringsnøkkelen som ble brukt eksport av data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Velg formatet til importfilen" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Denne oppføringen har gamle fil-vedlegg som må repareres." }, - "attachmentFixDesc": { - "message": "Dette er et gammelt fil-vedlegg som må repareres. Klikk for å se mer." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Reparer", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Bare organisasjonens hvelv knyttet til $ORGANIZATION$ vil bli eksportert. Personlige hvelvelementer og elementer fra andre organisasjoner vil ikke bli inkludert.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 69df4fe5647..8e7502dbcf0 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index faccc73593f..cb39f72a3b8 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Er is een onverwachte fout opgetreden." }, + "expirationDateError": { + "message": "Kies een vervaldatum in de toekomst." + }, "emailAddress": { "message": "E-mailadres" }, "yourVaultIsLocked": { "message": "Je kluis is vergrendeld. Voer je hoofdwachtwoord in om door te gaan." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Ontgrendelen" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Verificatiecode kopiëren" }, + "copyUuid": { + "message": "UUID kopiëren" + }, "warning": { "message": "Waarschuwing" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Fout bij het decoderen van het geëxporteerde bestand. Je encryptiesleutel komt niet overeen met de gebruikte sleutel waarmee de gegevens zijn geëxporteerd." }, + "importDestination": { + "message": "Importbestemming" + }, + "learnAboutImportOptions": { + "message": "Leer meer over je importopties" + }, + "selectImportFolder": { + "message": "Map selecteren" + }, + "selectImportCollection": { + "message": "Collectie selecteren" + }, + "importTargetHint": { + "message": "Kies deze optie als je de geïmporteerde bestandsinhoud wilt verplaatsen naar een $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Bestand bevat niet-toegewezen items." + }, "selectFormat": { "message": "Selecteer het formaat van het importbestand" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Dit item heeft oude bestandsbijlagen die aangepast moeten worden." }, - "attachmentFixDesc": { - "message": "Dit is een oude bestandsbijlage die moet worden aangepast. Klik voor meer informatie." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Oplossen", @@ -5429,7 +5463,7 @@ } } }, - "exportingOrganizationVaultDescription": { + "exportingOrganizationVaultDesc": { "message": "Exporteert alleen de organisatiekluis van $ORGANIZATION$. Geen persoonlijke kluis-items of items van andere organisaties.", "placeholders": { "organization": { @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Verplicht bestaande leden hun wachtwoord te wijzigen" }, - "region": { - "message": "Regio" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "VS", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Je hebt geen rechten om dit project te verwijderen", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Extra service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Bij je abonnement zijn $COUNT$ service accounts ingegrepen. Je kunt meer service accounts toevoegen voor $COST$ per maand.", + "includedServiceAccounts": { + "message": "Bij je abonnement horen $COUNT$ serviceaccounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Je kunt extra serviceaccounts toevoegen voor $COST$ per maand.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "De Secrets Manager Beta eindigde $BETA_ENDING_DATE$. Je hebt nog $DAYS$ dagen over voor het toevoegen van Secrets Manager aan je betaalde abonnement en toegang te houden tot de gegevens in Secrets Manager. Neem contact op met Customer Success voor het toevoegen van Secrets Manager aan je abonnement.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta beëindigd" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 3f5494a68cf..9023e2ddc77 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Eit uventa problem oppstod." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-postadresse" }, "yourVaultIsLocked": { "message": "Kvelvet ditt er låst. Stadfesta hovudpassordet ditt for å halda fram." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Lås opp" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Åtvaring" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 950467daf4a..56242ae7891 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Wystąpił nieoczekiwany błąd." }, + "expirationDateError": { + "message": "Wybierz datę wygaśnięcia, która przypada w przyszłości." + }, "emailAddress": { "message": "Adres e-mail" }, "yourVaultIsLocked": { "message": "Sejf jest zablokowany. Wpisz hasło główne, aby kontynuować." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Odblokuj" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopiuj kod weryfikacyjny" }, + "copyUuid": { + "message": "Skopiuj UUID" + }, "warning": { "message": "Ostrzeżenie" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Wystąpił błąd podczas odszyfrowywania pliku. Klucz szyfrowania nie pasuje do klucza użytego podczas eksportowania danych." }, + "importDestination": { + "message": "Miejsce docelowe importu" + }, + "learnAboutImportOptions": { + "message": "Dowiedz się o opcjach importu" + }, + "selectImportFolder": { + "message": "Wybierz folder" + }, + "selectImportCollection": { + "message": "Wybierz kolekcję" + }, + "importTargetHint": { + "message": "Wybierz tę opcję, jeśli chcesz, aby zawartość zaimportowanego pliku została przeniesiona do $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Plik zawiera nieprzypisane elementy." + }, "selectFormat": { "message": "Wybierz format importowanego pliku" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Ten element posiada stare załączniki, które muszą zostać naprawione." }, - "attachmentFixDesc": { - "message": "To jest stary załącznik, który musi zostać naprawiony. Kliknij, aby dowiedzieć się więcej." + "attachmentFixDescription": { + "message": "Ten załącznik używa przestarzałego szyfrowania. Wybierz 'Napraw', aby pobrać, ponownie zaszyfrować i ponownie przesłać załącznik." }, "fix": { "message": "Napraw", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Tylko sejf organizacji powiązany z organizacją $ORGANIZATION$ zostanie wyeksportowany. Osobiste elementy sejfu i elementy innych organizacji nie będą uwzględnione.", + "exportingOrganizationVaultDesc": { + "message": "Tylko sejf organizacji powiązany z $ORGANIZATION$ zostanie wyeksportowany. Pozycje w poszczególnych sejfach lub innych organizacji nie będą uwzględnione.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Wymagaj od istniejących członków zmiany haseł" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "UE", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Nie masz uprawnień do usunięcia tego projektu", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Dodatkowe konto serwisowe" }, - "additionalServiceAccountsDesc": { - "message": "W twoim planie możesz mieć $COUNT$ kont(a) serwisowe. Możesz dodać dodatkowe konta serwisowe za $COST$ miesięcznie.", + "includedServiceAccounts": { + "message": "Twój plan obejmuje $COUNT$ kont serwisowych.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Możesz dodać dodatkowe konta serwisowe za $COST$ miesięcznie.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Maksymalny potencjalny koszt kont serwisowych" + }, + "smBetaEndedDesc": { + "message": "Wersja beta menedżera sekretów zakończyła się dnia $BETA_ENDING_DATE$. Pozostało Ci $DAYS$ dni, aby dodać menedżera sekretów do swojej płatnej subskrypcji i zachować dostęp do danych menedżera sekretów. Skontaktuj się z obsługą klienta, aby dodać menedżera sekretów do swojej subskrypcji.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Zakończenie testowania bety" + }, + "beta": { + "message": "Wersja beta" } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 30e8021e3b1..a8f0cef6c19 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ocorreu um erro inesperado." }, + "expirationDateError": { + "message": "Por favor, selecione uma data de expiração que seja no futuro." + }, "emailAddress": { "message": "Endereço de e-mail" }, "yourVaultIsLocked": { "message": "O seu cofre está bloqueado. Verifique a sua senha mestra para continuar." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Desbloquear" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copiar Código de Verificação" }, + "copyUuid": { + "message": "Copiar URL" + }, "warning": { "message": "Aviso" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Erro ao descriptografar o arquivo exportado. Sua chave de criptografia não corresponde à chave de criptografia usada para exportar os dados." }, + "importDestination": { + "message": "Destino da Importação" + }, + "learnAboutImportOptions": { + "message": "Saiba mais sobre suas opções de importação" + }, + "selectImportFolder": { + "message": "Selecione uma pasta" + }, + "selectImportCollection": { + "message": "Selecione uma coleção" + }, + "importTargetHint": { + "message": "Selecione esta opção se você quer o conteúdo do arquivo importado movido para $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Arquivo contém itens não atribuídos." + }, "selectFormat": { "message": "Selecione o formato do arquivo de importação" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Este item tem anexos de arquivos antigos que precisam ser corrigidos." }, - "attachmentFixDesc": { - "message": "Este é um anexo de arquivo antigo que precisa ser corrigido. Clique para saber mais." + "attachmentFixDescription": { + "message": "Este anexo utiliza criptografia desatualizada. Selecione 'Corrigir' para baixar, re-criptografar e recarregar o anexo." }, "fix": { "message": "Corrigir", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Apenas o cofre da organização associado com $ORGANIZATION$ será exportado. Itens do cofre pessoal e itens de outras organizações não serão incluídos.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Região" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "O seu plano vem com $COUNT$ contas de serviço.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Você pode adicionar contas de serviço adicionais por $COST$ mensais.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "O Gerenciador de Segredos Beta terminou em $BETA_ENDING_DATE$. Você tem $DAYS$ dias para adicionar o Gerenciador de Segredos à sua assinatura paga e manter o acesso aos dados do Gerenciador Secretos. Contate o setor de Sucesso do Cliente para adicionar o Gerenciador de Segredos à sua assinatura.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Final da versão Beta" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 8a866881720..1e9e80a7de3 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ocorreu um erro inesperado." }, + "expirationDateError": { + "message": "Por favor, selecione uma data de expiração futura." + }, "emailAddress": { "message": "Endereço de e-mail" }, "yourVaultIsLocked": { "message": "O seu cofre está bloqueado. Verifique a sua palavra-passe mestra para continuar." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Desbloquear" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copiar código de verificação" }, + "copyUuid": { + "message": "Copiar UUID" + }, "warning": { "message": "Aviso" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Erro ao desencriptar o ficheiro exportado. A sua chave de encriptação não corresponde à chave de encriptação utilizada para exportar os dados." }, + "importDestination": { + "message": "Destino da importação" + }, + "learnAboutImportOptions": { + "message": "Saiba mais sobre as suas opções de importação" + }, + "selectImportFolder": { + "message": "Selecionar uma pasta" + }, + "selectImportCollection": { + "message": "Selecionar uma coleção" + }, + "importTargetHint": { + "message": "Selecione esta opção se pretender que o conteúdo do ficheiro importado seja transferido para $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "O ficheiro contém itens não atribuídos." + }, "selectFormat": { "message": "Selecione o formato do ficheiro a importar" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Este item tem anexos de ficheiros antigos que precisam de ser corrigidos." }, - "attachmentFixDesc": { - "message": "Trata-se de um anexo de ficheiro antigos que precisa de ser corrigido. Clique para saber mais." + "attachmentFixDescription": { + "message": "Este anexo utiliza uma encriptação desatualizada. Selecione \"Corrigir\" para transferir, voltar a encriptar e voltar a carregar o anexo." }, "fix": { "message": "Corrigir", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Apenas o cofre da organização associado a $ORGANIZATION$ serão exportados. Os itens do cofre pessoal e os de outras organizações não serão incluídos.", + "exportingOrganizationVaultDesc": { + "message": "Apenas o cofre da organização associado a $ORGANIZATION$ será exportado. Os itens em cofres individuais ou noutras organizações não serão incluídos.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Exigir que os membros existentes alterem as suas palavras-passe" }, - "region": { - "message": "Região" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "UE", - "description": "European Union" - }, - "us": { - "message": "EUA", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Não tem permissões para eliminar este projeto", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Contas de serviço adicionais" }, - "additionalServiceAccountsDesc": { - "message": "O seu plano é fornecido com $COUNT$ contas de serviço. Pode adicionar contas de serviço adicionais por $COST$ por mês.", + "includedServiceAccounts": { + "message": "O seu plano inclui $COUNT$ contas de serviço.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Pode adicionar contas de serviço adicionais por $COST$ por mês.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Custo máximo potencial da conta de serviço" + }, + "smBetaEndedDesc": { + "message": "O Gestor de Segredos Beta terminou a $BETA_ENDING_DATE$. Restam-lhe $DAYS$ dias para adicionar o Gestor de Segredos à sua subscrição paga e manter o acesso aos dados do Gestor de Segredos. Contacte o Serviço de Apoio ao Cliente para adicionar o Gestor de Segredos à sua subscrição.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Fim da versão Beta" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 4c78e940f5e..ab162e68fcb 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "A survenit o eroare neașteptată." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Adresă de e-mail" }, "yourVaultIsLocked": { "message": "Seiful dvs. este blocat. Verificați parola principală pentru a continua." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Deblocare" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copiere cod de verificare" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Avertisment" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Eroare la decriptarea fișierului exportat. Cheia dvs. de criptare nu corespunde cu cheia de criptare folosită pentru a exporta datele." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Alegeți din listă formatul fișierului de import" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Acest element are atașamente vechi care trebuie fixate." }, - "attachmentFixDesc": { - "message": "Acesta este un atașament de fișier vechi care trebuie reparat. Clicați pentru a afla mai multe." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Repară", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Numai seiful organizației asociat cu $ORGANIZATION$ va fi exportat. Articolele de seif individuale și articolele din alte organizații nu vor fi incluse.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 43cf40c373d..ac6590b76dc 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Произошла непредвиденная ошибка." }, + "expirationDateError": { + "message": "Выберите дату истечения срока действия в будущем." + }, "emailAddress": { "message": "Адрес email" }, "yourVaultIsLocked": { "message": "Ваше хранилище заблокировано. Для продолжения введите мастер-пароль." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Разблокировать" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Скопировать код подтверждения" }, + "copyUuid": { + "message": "Скопировать UUID" + }, "warning": { "message": "Предупреждение" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Ошибка при расшифровке экспортированного файла. Ваш ключ шифрования не совпадает с ключом шифрования, используемым для экспорта данных." }, + "importDestination": { + "message": "Направление импорта" + }, + "learnAboutImportOptions": { + "message": "Узнайте о возможностях импорта" + }, + "selectImportFolder": { + "message": "Выбрать папку" + }, + "selectImportCollection": { + "message": "Выбрать коллекцию" + }, + "importTargetHint": { + "message": "Выберите эту опцию, если хотите, чтобы содержимое импортированного файла было перемещено в папку $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Выберите формат файла импорта" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "К этому элементу прикреплены старые вложения, которые необходимо исправить." }, - "attachmentFixDesc": { - "message": "Это старое вложение необходимо исправить. Нажмите, чтобы узнать больше." + "attachmentFixDescription": { + "message": "Вложение использует устаревшее шифрование. Выберите 'Исправить' дня загрузки, повторного шифрования и выгрузки вложения." }, "fix": { "message": "Исправить", @@ -4705,7 +4739,7 @@ "message": "Ошибка" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "Для управления пользователями также необходимо предоставить разрешение на управление восстановлением аккаунта" }, "setupProvider": { "message": "Настройка поставщика" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Будет экспортировано только хранилище организации, связанное с $ORGANIZATION$. Личные элементы хранилища и элементы из других организаций включены не будут.", + "exportingOrganizationVaultDesc": { + "message": "Будет экспортировано только хранилище организации, связанное с $ORGANIZATION$. Элементы из личных хранилищ и из других организаций включены не будут.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Требовать от существующих пользователей смены паролей" }, - "region": { - "message": "Регион" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "Европа", - "description": "European Union" - }, - "us": { - "message": "США", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "У вас недостаточно прав для удаления этого проекта", @@ -6825,7 +6854,7 @@ "message": "Обновить параметры KDF" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Доверенные устройства" }, "memberDecryptionTdeDescriptionPartOne": { "message": "После аутентификации участники расшифровывают данные хранилища с помощью ключа, хранящегося на их устройстве.", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Дополнительные сервисные аккаунты" }, - "additionalServiceAccountsDesc": { - "message": "Ваш тарифный план включает в себя $COUNT$ сервисных аккаунтов. Вы можете добавить дополнительные сервисные аккаунты за $COST$ в месяц.", + "includedServiceAccounts": { + "message": "Ваш план включает в себя $COUNT$ сервисных аккаунтов.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Вы можете добавить дополнительные сервисные аккаунты за $COST$ в месяц.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Максимальная потенциальная стоимость сервисного аккаунта" + }, + "smBetaEndedDesc": { + "message": "Бета-версия Менеджера секретов завершилась $BETA_ENDING_DATE$. У вас осталось $DAYS$ дней, чтобы добавить его к своей платной подписке и сохранить доступ к данным. Чтобы добавить Менеджер секретов к подписке, обратитесь в службу поддержки клиентов.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Окончание бета-версии" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 740f98921a8..e70b94f4d6e 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "වි-තැපැල් ලිපිනය" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "අනවහිර" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "අවවාදයයි" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index d05808c109b..15ab9567eb9 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Vyskytla sa neočakávaná chyba." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Emailová adresa" }, "yourVaultIsLocked": { "message": "Váš trezor je uzamknutý. Overte sa hlavným heslom ak chcete pokračovať." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Odomknúť" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopírovať overovací kód" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Upozornenie" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Chyba pri dešifrovaní exportovaného súboru. Váš šifrovací kľúč sa nezhoduje so šifrovacím kľúčom použitým pri exporte údajov." }, + "importDestination": { + "message": "Cieľ importu" + }, + "learnAboutImportOptions": { + "message": "Zistiť viac o možnostiach importu" + }, + "selectImportFolder": { + "message": "Vyberte priečinok" + }, + "selectImportCollection": { + "message": "Vyberte zbierku" + }, + "importTargetHint": { + "message": "Zvoľte túto možnosť ak chcete obsah importovaného súboru presunúť do $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Súbor obsahuje nepridelené položky." + }, "selectFormat": { "message": "Vyberte formát súboru importu" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Táto položka má staré prílohy, ktoré je potrebné opraviť." }, - "attachmentFixDesc": { - "message": "Táto stará príloha musí byť opravená. Kliknite ak sa chcete dozvedieť viac." + "attachmentFixDescription": { + "message": "Táto príloha používa zastaralý systém šifrovania. Zvoľte „Opraviť“ pre stiahnutie, opätovné zašifrovanie a nahranie prílohy." }, "fix": { "message": "Opraviť", @@ -5429,7 +5463,7 @@ } } }, - "exportingOrganizationVaultDescription": { + "exportingOrganizationVaultDesc": { "message": "Exportované budú iba položky trezora organizácie spojené s $ORGANIZATION$. Položky osobného trezora a položky z iných organizácií nebudú zahrnuté.", "placeholders": { "organization": { @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Požadovať po súčasných členoch, aby si zmenili heslo" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Na odstránenie tohto projektu nemáte oprávnenia", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Dodatočné služobné kontá" }, - "additionalServiceAccountsDesc": { - "message": "Váš plán zahŕňa $COUNT$ služobných kont. Ďalšie služobné kontá môžete pridať za $COST$ mesačne.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Maximálna možná cena služobných kont" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 2fcb958d0fd..2d12a70086f 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Prišlo je do nepričakovane napake." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-poštni naslov" }, "yourVaultIsLocked": { "message": "Vaš trezor je zaklenjen. Potrdite vaše glavno geslo za nadaljevanje." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Odkleni" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopiraj verifikacijsko kodo" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Opozorilo" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Dešifriranje izvožene datoteke je spodletelo. Ključ za dešifriranje se ne ujema s ključem, ki je bil uporabljen pri izvozu." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Izberite format datoteke za uvoz" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Regija" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index bab17c39a82..b7ecce559ab 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Дошло је до неочекиване грешке." }, + "expirationDateError": { + "message": "Изаберите датум истека који је у будућности." + }, "emailAddress": { "message": "Имејл" }, "yourVaultIsLocked": { "message": "Сеф је блокиран. Унесите главну лозинку за наставак." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Откључај" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Копирај верификациони код" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Упозорење" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Грешка у дешифрирању извозне датотеке. Ваш кључ за шифровање не одговара кључу који се користио за извоз података." }, + "importDestination": { + "message": "Смештај увоза" + }, + "learnAboutImportOptions": { + "message": "Сазнајте више о опцијама увоза" + }, + "selectImportFolder": { + "message": "Изабери фасциклу" + }, + "selectImportCollection": { + "message": "Изабери колекцију" + }, + "importTargetHint": { + "message": "Изаберите ову опцију ако желите да се садржај увезене датотеке премести у $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Датотека садржи недодељене ставке." + }, "selectFormat": { "message": "Одабрати формат датотеке за увоз" }, @@ -2636,10 +2670,10 @@ "message": "Интернет Сеф" }, "bitWebVault": { - "message": "Bitwarden Web vault" + "message": "Bitwarden Интернет Сеф" }, "bitSecretsManager": { - "message": "Bitwarden Secrets Manager" + "message": "Bitwarden Менаџер Тајности" }, "loggedIn": { "message": "Пријављено." @@ -3320,7 +3354,7 @@ "message": "Поставите ограничење лиценце за своју претплату. Једном када се достигне ова граница, нећете моћи да позовете нове кориснике." }, "limitSmSubscriptionDesc": { - "message": "Set a seat limit for your Secrets Manger subscription. Once this limit is reached, you will not be able to invite new members." + "message": "Поставите ограничење лиценце за своју претплату менаџера тајни. Једном када се достигне ова граница, нећете моћи да позовете нове чланове." }, "maxSeatLimit": { "message": "Максимална граница лиценце (опционо)", @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Ова ставка има старе прилоге које треба поправити." }, - "attachmentFixDesc": { - "message": "Ово је стари прилог који треба поправити. Кликните да бисте сазнали више." + "attachmentFixDescription": { + "message": "Овај прилог користи застарело шифровање. Изаберите „Поправи“ да бисте преузели, поново шифровали и поново отпремили прилог." }, "fix": { "message": "Фиксирај", @@ -4375,7 +4409,7 @@ "message": "Управљај корисницима" }, "manageAccountRecovery": { - "message": "Manage account recovery" + "message": "Управљајте опоравком налога" }, "disableRequiredError": { "message": "Морате ручно да онемогућите $POLICYNAME$ пријаву пре него што ова политика може да се онемогући.", @@ -4705,7 +4739,7 @@ "message": "Грешка" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "Корисницима за управљање такође мора бити додељена дозвола за опоравак налога за управљање" }, "setupProvider": { "message": "Подешавање првајдера" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Биће извезен само сеф организације повезан са $ORGANIZATION$. Ставке у појединачним сефовима или другим организацијама неће бити укључене.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Захтевајте од постојећих чланова да промене њихове лозинке" }, - "region": { - "message": "Регион" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Немате дозволе да избришете овај пројекат", @@ -6825,7 +6854,7 @@ "message": "Ажурирати KDF подешавања" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Поуздани уређаји" }, "memberDecryptionTdeDescriptionPartOne": { "message": "Када се аутентификују, чланови ће дешифровати податке из сефљ користећи кључ сачуван на њиховом уређају", @@ -6956,15 +6985,15 @@ "message": "Одабрана застава" }, "sendsNoItemsTitle": { - "message": "No active Sends", + "message": "Нема активних Sends", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", + "message": "Употребите Send да безбедно делите шифроване информације са било ким.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inviteUsers": { - "message": "Invite Users" + "message": "Позови кориснике" }, "secretsManagerForPlan": { "message": "Secrets Manager for $PLAN$", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Ваш план долази са $COUNT$ налога сервиса.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "Можете додати још сервисних налога за $COST$ месечно.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7043,7 +7077,7 @@ "message": "Secrets Manager plan price" }, "passwordManager": { - "message": "Password Manager" + "message": "Менаџер лозинки" }, "freeOrganization": { "message": "Free Organization" @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "Бета менаџера тајни се завршио $BETA_ENDING_DATE$. Остало вам је $DAYS$ дана да додате Менаџер тајни у вашу претплату и да задржите приступ подацима Манагера тајни. Контактирајте подршку да додате Менаџер тајни у своју претплату.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Крај бета" + }, + "beta": { + "message": "Бета" } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 83ec7eaa952..5d393e10ef8 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Imejl Adresa" }, "yourVaultIsLocked": { "message": "Vaš trezor je zaključan. Unesite glavnu lozinku da biste nastavili." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Otključaj" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Upozorenje" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Izaberite format datoteke za uvoz" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 736bd83e0ca..65c4890363e 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Ett oväntat fel har inträffat." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-postadress" }, "yourVaultIsLocked": { "message": "Valvet är låst. Bekräfta ditt huvudlösenord för att fortsätta." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Lås upp" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Kopiera verifieringskod" }, + "copyUuid": { + "message": "Kopiera UUID" + }, "warning": { "message": "Varning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Ett fel uppstod vid dekryptering av den exporterade filen. Din krypteringsnyckel matchar inte krypteringsnyckeln som användes för att exportera datan." }, + "importDestination": { + "message": "Importdestination" + }, + "learnAboutImportOptions": { + "message": "Läs mer om dina importalternativ" + }, + "selectImportFolder": { + "message": "Välj en mapp" + }, + "selectImportCollection": { + "message": "Välj en samling" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "Filen innehåller otilldelade objekt." + }, "selectFormat": { "message": "Välj importfilens format" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Detta objekt har gamla bilagor som behöver åtgärdas." }, - "attachmentFixDesc": { - "message": "Detta är en gammal bilaga som behöver åtgärdas. Klicka för att läsa mer." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Åtgärda", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -5491,7 +5525,7 @@ "message": "Generera användarnamn" }, "usernameType": { - "message": "Username type" + "message": "Användarnamnstyp" }, "plusAddressedEmail": { "message": "Plus addressed email", @@ -5805,7 +5839,7 @@ "message": "Inga objekt hittades" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Rensa alla" }, "toggleCharacterCount": { "message": "Toggle character count", @@ -5999,11 +6033,11 @@ "description": "Title for creating a new project." }, "projectEdited": { - "message": "Project edited", + "message": "Projekt har redigerats", "description": "Notification for the successful editing of a project." }, "projectSaved": { - "message": "Project saved", + "message": "Projekt har sparats", "description": "Notification for the successful saving of a project." }, "projectCreated": { @@ -6545,7 +6579,7 @@ "message": "If you do not want to opt into billing sync, manually upload your license here." }, "syncLicense": { - "message": "Sync license" + "message": "Synkronisera licens" }, "licenseSyncSuccess": { "message": "Successfully synced license" @@ -6605,7 +6639,7 @@ "message": "Can read, write" }, "groupSlashUser": { - "message": "Group/User" + "message": "Grupp/Användare" }, "lowKdfIterations": { "message": "Low KDF Iterations" @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 6c9b0111a67..cc3bc7427a2 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Email address" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your master password to continue." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Unlock" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Warning" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 23e6b3fd1c2..9d2df6b8709 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "ข้อผิดพลาดที่ไม่คาดคิดเกิดขึ้น" }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "ที่อยู่อีเมล์" }, "yourVaultIsLocked": { "message": "ตู้เซฟของคุณถูกล็อค ใส่รหัสผ่านหลักของคุณเพื่อดำเนินการต่อ" }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "ปลดล็อค" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Copy verification code" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "คำเตือน" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Select the format of the import file" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 208083843be..be77f99ed4d 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Beklenmedik bir hata oluştu." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "E-posta adresi" }, "yourVaultIsLocked": { "message": "Kasanız kilitli. Devam etmek için ana parolanızı doğrulayın." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Kilidi aç" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Doğrulama kodunu kopyala" }, + "copyUuid": { + "message": "UUID'yi kopyala" + }, "warning": { "message": "Uyarı" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Dışa aktarılmış dosya çözülemedi. Şifreleme anahtarınız, veri dışa aktarılırken kullanılanla uyuşmuyor." }, + "importDestination": { + "message": "İçe aktarma hedefi" + }, + "learnAboutImportOptions": { + "message": "İçe aktarma seçeneklerinizi öğrenin" + }, + "selectImportFolder": { + "message": "Bir klasör seçin" + }, + "selectImportCollection": { + "message": "Bir koleksiyon seçin" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "İçe aktarma dosyasının biçimini seçin" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Bu kayıtta düzeltilmesi gereken eski dosya ekleri bulunuyor." }, - "attachmentFixDesc": { - "message": "Bu eski dosya ekinin düzeltmesi gerekiyor. Daha fazla bilgi için tıklayın." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Düzelt", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Yalnızca $ORGANIZATION$ ile ilişkili kuruluş kasası dışarı aktarılacak. Kişisel kasadaki ögeler ve diğer kuruluşlardaki ögeler dahil edilmeyecek.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Mevcut üyelerin parolalarını değiştirmelerini zorunlu tut" }, - "region": { - "message": "Bölge" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "AB", - "description": "European Union" - }, - "us": { - "message": "ABD", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "Bu projeyi silme izniniz yok", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta sona ediyor" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 4479545ab91..36702e5bd24 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Сталася неочікувана помилка." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Адреса е-пошти" }, "yourVaultIsLocked": { "message": "Сховище заблоковано. Введіть головний пароль для продовження." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Розблокувати" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Копіювати код підтвердження" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Попередження" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Помилка розшифрування експортованого файлу. Ваш ключ шифрування відрізняється від ключа, використаного для експортування даних." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Оберіть формат імпортованого файлу" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "Цей елемент має старі вкладені файли, які необхідно виправити." }, - "attachmentFixDesc": { - "message": "Цей старий вкладений файл необхідно виправити. Натисніть, щоб дізнатися більше." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Виправити", @@ -4705,7 +4739,7 @@ "message": "Помилка" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "Разом із дозволом на керування відновленням облікового запису також необхідно надати дозвіл на керування користувачами" }, "setupProvider": { "message": "Налаштування постачальника" @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Будуть експортовані лише записи сховища організації, пов'язані з $ORGANIZATION$. Записи особистого сховища та записи з інших організацій не буде включено.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Вимагати від наявних учасників змінювати паролі" }, - "region": { - "message": "Регіон" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "ЄС", - "description": "European Union" - }, - "us": { - "message": "США", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "У вас немає дозволу на видалення цього проєкту", @@ -6825,7 +6854,7 @@ "message": "Оновити налаштування KDF" }, "trustedDevices": { - "message": "Trusted devices" + "message": "Довірені пристрої" }, "memberDecryptionTdeDescriptionPartOne": { "message": "Після авторизації учасники розшифровуватимуть дані сховища з використанням ключа, збереженого на їхньому пристрої.", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Додаткові службові облікові записи" }, - "additionalServiceAccountsDesc": { - "message": "Ваш план включає $COUNT$ службових облікових записів. Ви можете додати більше службових облікових записів за $COST$ на місяць.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Потенційна максимальна вартість службового облікового запису" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 6bd4716281f..c4c78182bef 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "Một lỗi bất ngờ đã xảy ra." }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "Địa chỉ email" }, "yourVaultIsLocked": { "message": "Kho của bạn đã bị khóa. Xác minh mật khẩu chính của bạn để tiếp tục." }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "Mở khóa" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "Sao chép mã xác thực" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "Cảnh báo" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "Chọn định dạng cho file xuất" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "This item has old file attachments that need to be fixed." }, - "attachmentFixDesc": { - "message": "This is an old file attachment the needs to be fixed. Click to learn more." + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "Fix", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Individual vault items and items from other organizations will not be included.", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "Region" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "EU", - "description": "European Union" - }, - "us": { - "message": "US", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "Additional service accounts" }, - "additionalServiceAccountsDesc": { - "message": "Your plan comes with $COUNT$ service accounts. You can add additional service accounts for $COST$ per month.", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "Max potential service account cost" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 104f7439c33..76a0af2bc0c 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "发生意外错误。" }, + "expirationDateError": { + "message": "请选择一个过期日期(必须是将来的某个日期)。" + }, "emailAddress": { "message": "电子邮件地址" }, "yourVaultIsLocked": { "message": "您的密码库已锁定,请验证您的主密码以继续。" }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "解锁​​​​" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "复制验证码" }, + "copyUuid": { + "message": "复制 UUID" + }, "warning": { "message": "警告" }, @@ -1000,7 +1009,7 @@ "message": "确认文件密码" }, "accountRestrictedOptionDescription": { - "message": "使用衍生自您账户的用户名和主密码的加密密钥,以加密此导出并限制只能导入到当前的 Bitwarden 帐户。" + "message": "使用衍生自您账户的用户名和主密码的加密密钥,以加密此导出并限制只能导入到当前的 Bitwarden 账户。" }, "passwordProtectedOptionDescription": { "message": "设置一个密码用来加密导出的数据,并使用此密码解密以导入到任意 Bitwarden 账户。" @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "解密导出的文件时出错。您的加密密钥与导出数据时使用的加密密钥不匹配。" }, + "importDestination": { + "message": "导入目的地" + }, + "learnAboutImportOptions": { + "message": "了解您的导入选项" + }, + "selectImportFolder": { + "message": "选择一个文件夹" + }, + "selectImportCollection": { + "message": "选择一个集合" + }, + "importTargetHint": { + "message": "如果您希望将导入的文件内容移至 $DESTINATION$,请选中此选项", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "文件包含未分配项目。" + }, "selectFormat": { "message": "选择导入文件的格式" }, @@ -1959,7 +1993,7 @@ "message": "# GB 附加存储" }, "additionalStorageIntervalDesc": { - "message": "您的计划附带 $SIZE$ 的加密存储空间。您也可以以 $PRICE$ 每 GB 每 $INTERVAL$ 购买附加存储。", + "message": "您的计划包含 $SIZE$ 的加密存储空间。您也可以以 $PRICE$ 每 GB 每 $INTERVAL$ 购买附加存储。", "placeholders": { "size": { "content": "$1", @@ -3590,15 +3624,15 @@ "attachmentsNeedFix": { "message": "此项目有需要修复的旧文件附件。" }, - "attachmentFixDesc": { - "message": "这是一个需要修复的旧文件附件。点击了解更多。" + "attachmentFixDescription": { + "message": "此附件使用了过时的加密方式。选择「修复」以下载、重新加密,然后重新上传此附件。" }, "fix": { "message": "修复", "description": "This is a verb. ex. 'Fix The Car'" }, "oldAttachmentsNeedFixDesc": { - "message": "需要先修复密码库中的旧文件附件,然后才能轮换账户的加密密钥。" + "message": "需要先修复您的密码库中的旧文件附件,然后才能轮换您账户的加密密钥。" }, "yourAccountsFingerprint": { "message": "您的账户的指纹短语", @@ -4705,7 +4739,7 @@ "message": "错误" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "管理用户也必须被授予管理账户恢复的权限" }, "setupProvider": { "message": "提供商设置" @@ -5238,7 +5272,7 @@ "message": "Key Connector" }, "memberDecryptionKeyConnectorDescStart": { - "message": "连接 SSO 登录到您的自托管解密密钥服务器。使用此选项后,成员将不再需要使用主密码解密密码库数据。", + "message": "连接 SSO 登录到您的自托管解密密钥服务器。使用此选项后,成员将不再需要使用主密码解密密码库数据。需要", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "memberDecryptionKeyConnectorDescLink": { @@ -5246,7 +5280,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "memberDecryptionKeyConnectorDescEnd": { - "message": "被用于设置 Key Connector 解密。请联系 Bitwarden 支持获取协助。", + "message": "以用于设置 Key Connector 解密。请联系 Bitwarden 支持获取协助。", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "keyConnectorPolicyRestriction": { @@ -5325,7 +5359,7 @@ "message": "轮换令牌" }, "rotateBillingSyncTokenWarning": { - "message": "要继续,您需要重新设置您的自托管服务器上的计费同步功能。" + "message": "继续的话,您需要重新设置您的自托管服务器上的计费同步功能。" }, "rotateBillingSyncTokenTitle": { "message": "轮换计费同步令牌将使之前的令牌失效。" @@ -5421,7 +5455,7 @@ "message": "正在导出组织密码库" }, "exportingPersonalVaultDescription": { - "message": "仅会导出与 $EMAIL$ 关联的个人密码库项目,不包括组织密码库的项目。仅会导出密码库项目信息,不包括关联的密码历史记录或附件。", + "message": "仅会导出与 $EMAIL$ 关联的个人密码库项目,不包括组织密码库的项目。仅会导出密码库项目信息,不包括关联的密码历史记录和附件。", "placeholders": { "email": { "content": "$1", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "仅会导出与 $ORGANIZATION$ 关联的组织密码库。个人或其他组织的密码库项目不会导出。", + "exportingOrganizationVaultDesc": { + "message": "仅会导出与 $ORGANIZATION$ 关联的组织密码库数据。不包括个人密码库和其他组织中的项目。", "placeholders": { "organization": { "content": "$1", @@ -6512,7 +6546,7 @@ "message": "正在导出组织机密数据" }, "exportingOrganizationSecretDataDescription": { - "message": "仅会导出与 $ORGANIZATION$ 关联的机密管理器数据。不包括其他产品中的项目或来自其他组织的项目。", + "message": "仅会导出与 $ORGANIZATION$ 关联的机密管理器数据。不包括其他产品中的项目和来自其他组织的项目。", "placeholders": { "ORGANIZATION": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "要求现有成员更改他们的密码" }, - "region": { - "message": "区域" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "欧盟", - "description": "European Union" - }, - "us": { - "message": "美国", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "您无权删除此工程", @@ -6828,7 +6857,7 @@ "message": "受信任的设备" }, "memberDecryptionTdeDescriptionPartOne": { - "message": "验证后,成员将使用存储在他们的设备上的密钥解密密码库数据。使用此选项后,", + "message": "验证后,成员将使用存储在他们设备上的密钥解密密码库数据。使用此选项后,", "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 and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionTdeDescriptionLinkOne": { @@ -6967,7 +6996,7 @@ "message": "邀请用户" }, "secretsManagerForPlan": { - "message": "用于 $PLAN$ 的机密管理器", + "message": "适用于 $PLAN$ 的机密管理器", "placeholders": { "plan": { "content": "$1", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "附加服务账户" }, - "additionalServiceAccountsDesc": { - "message": "您的计划包含 $COUNT$ 个服务账户。您也可以以 $COST$ 每月购买附加服务账户。", + "includedServiceAccounts": { + "message": "您的计划包含 $COUNT$ 个服务账户。", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "您可以以 $COST$ 每月购买附加服务账户。", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "最大潜在服务账户费用" + }, + "smBetaEndedDesc": { + "message": "机密管理器 Beta 版已于 $BETA_ENDING_DATE$ 结束。您还剩余 $DAYS$ 天时间将机密管理器添加到您的付费订阅中,以保持对其数据的访问权限。联系客户成功团队将机密管理器添加到您的订阅中。", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta 版即将结束" + }, + "beta": { + "message": "Beta" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 783b1c8fca4..fff4ae12c6e 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -711,12 +711,18 @@ "unexpectedError": { "message": "發生了未預期的錯誤。" }, + "expirationDateError": { + "message": "Please select an expiration date that is in the future." + }, "emailAddress": { "message": "電子郵件地址" }, "yourVaultIsLocked": { "message": "密碼庫已鎖定。請驗證主密碼以繼續。" }, + "uuid": { + "message": "UUID" + }, "unlock": { "message": "解鎖" }, @@ -948,6 +954,9 @@ "copyVerificationCode": { "message": "複製驗證碼" }, + "copyUuid": { + "message": "Copy UUID" + }, "warning": { "message": "警告" }, @@ -1284,6 +1293,31 @@ "importEncKeyError": { "message": "解密匯出的檔案時發生錯誤,您的加密金鑰與匯出資料時使用的金鑰不同。" }, + "importDestination": { + "message": "Import destination" + }, + "learnAboutImportOptions": { + "message": "Learn about your import options" + }, + "selectImportFolder": { + "message": "Select a folder" + }, + "selectImportCollection": { + "message": "Select a collection" + }, + "importTargetHint": { + "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", + "placeholders": { + "destination": { + "content": "$1", + "example": "folder or collection" + } + } + }, + "importUnassignedItemsError": { + "message": "File contains unassigned items." + }, "selectFormat": { "message": "選擇匯入檔案的格式" }, @@ -3590,8 +3624,8 @@ "attachmentsNeedFix": { "message": "此項目包含需要修正的舊檔案附件。" }, - "attachmentFixDesc": { - "message": "這是一個需要修正的舊檔案附件。點選此處以深入了解。" + "attachmentFixDescription": { + "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." }, "fix": { "message": "修正", @@ -5429,8 +5463,8 @@ } } }, - "exportingOrganizationVaultDescription": { - "message": "只會匯出與 $ORGANIZATION$ 關聯的組織密碼庫。個人密碼庫和其他組織的項目不包含在內。", + "exportingOrganizationVaultDesc": { + "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", "placeholders": { "organization": { "content": "$1", @@ -6802,16 +6836,11 @@ "enforceOnLoginDesc": { "message": "Require existing members to change their passwords" }, - "region": { - "message": "區域" + "usDomain": { + "message": "bitwarden.com" }, - "eu": { - "message": "歐盟", - "description": "European Union" - }, - "us": { - "message": "美國", - "description": "United States" + "euDomain": { + "message": "bitwarden.eu" }, "smProjectDeleteAccessRestricted": { "message": "You don't have permissions to delete this project", @@ -7023,15 +7052,20 @@ "additionalServiceAccounts": { "message": "新增服務帳號" }, - "additionalServiceAccountsDesc": { - "message": "您的方案內含 $COUNT$ 組服務帳號,可再以每月 $COST$ 費用新增更多服務帳號。", + "includedServiceAccounts": { + "message": "Your plan comes with $COUNT$ service accounts.", "placeholders": { "count": { "content": "$1", "example": "50" - }, + } + } + }, + "addAdditionalServiceAccounts": { + "message": "You can add additional service accounts for $COST$ per month.", + "placeholders": { "cost": { - "content": "$2", + "content": "$1", "example": "$0.50" } } @@ -7059,5 +7093,24 @@ }, "maxServiceAccountCost": { "message": "可能的最大服務帳號成本" + }, + "smBetaEndedDesc": { + "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "placeholders": { + "beta_ending_date": { + "content": "$1", + "example": "August 1, 2023" + }, + "days": { + "content": "$2", + "example": "11" + } + } + }, + "betaEnding": { + "message": "Beta Ending" + }, + "beta": { + "message": "Beta" } } From 3a2d89c9488976a2f07f97c2dbdb21e8837efe27 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Tue, 15 Aug 2023 09:28:05 -0400 Subject: [PATCH 013/135] [PM-2597] Do not show the notification banner on the configured bitwarden vault domain (#5863) * ignore TLD when checking for no banner display on a vault page * do not show the notification banner on the configured bitwarden vault domain * add types --- .../src/autofill/content/notification-bar.ts | 110 ++++++++++-------- apps/browser/src/autofill/types/index.ts | 41 +++++++ 2 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 apps/browser/src/autofill/types/index.ts diff --git a/apps/browser/src/autofill/content/notification-bar.ts b/apps/browser/src/autofill/content/notification-bar.ts index 41d8f145600..5bccd1d2a22 100644 --- a/apps/browser/src/autofill/content/notification-bar.ts +++ b/apps/browser/src/autofill/content/notification-bar.ts @@ -3,6 +3,7 @@ import ChangePasswordRuntimeMessage from "../../background/models/changePassword import AutofillField from "../models/autofill-field"; import { WatchedForm } from "../models/watched-form"; import { FormData } from "../services/abstractions/autofill.service"; +import { UserSettings } from "../types"; interface HTMLElementWithFormOpId extends HTMLElement { formOpId: string; @@ -26,10 +27,64 @@ interface HTMLElementWithFormOpId extends HTMLElement { * and async scripts to finish loading. * https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event */ -document.addEventListener("DOMContentLoaded", (event) => { - // Do not show the notification bar on the Bitwarden vault - // because they can add logins and change passwords there - if (window.location.hostname.endsWith("vault.bitwarden.com")) { +document.addEventListener("DOMContentLoaded", async (event) => { + // These are preferences for whether to show the notification bar based on the user's settings + // and they are set in the Settings > Options page in the browser extension. + let disabledAddLoginNotification = false; + let disabledChangedPasswordNotification = false; + let showNotificationBar = true; + + // Look up the active user id from storage + const activeUserIdKey = "activeUserId"; + let activeUserId: string; + await chrome.storage.local.get(activeUserIdKey, (obj: any) => { + if (obj == null || obj[activeUserIdKey] == null) { + return; + } + activeUserId = obj[activeUserIdKey]; + }); + + // Look up the user's settings from storage + await chrome.storage.local.get(activeUserId, (obj: any) => { + if (obj?.[activeUserId] == null) { + return; + } + + const userSettings: UserSettings = obj[activeUserId].settings; + + // Do not show the notification bar on the Bitwarden vault + // because they can add logins and change passwords there + if (window.location.origin === userSettings.serverConfig.environment.vault) { + showNotificationBar = false; + + return; + } + + // NeverDomains is a dictionary of domains that the user has chosen to never + // show the notification bar on (for login detail collection or password change). + // It is managed in the Settings > Excluded Domains page in the browser extension. + // Example: '{"bitwarden.com":null}' + const excludedDomainsDict = userSettings.neverDomains; + + if ( + excludedDomainsDict != null && + // eslint-disable-next-line + excludedDomainsDict.hasOwnProperty(window.location.hostname) + ) { + return; + } + + // Set local disabled preferences + disabledAddLoginNotification = userSettings.disableAddLoginNotification; + disabledChangedPasswordNotification = userSettings.disableChangedPasswordNotification; + + if (!disabledAddLoginNotification || !disabledChangedPasswordNotification) { + // If the user has not disabled both notifications, then handle the initial page change (null -> actual page) + handlePageChange(); + } + }); + + if (!showNotificationBar) { return; } @@ -77,53 +132,6 @@ document.addEventListener("DOMContentLoaded", (event) => { ]); const changePasswordButtonContainsNames = new Set(["pass", "change", "contras", "senha"]); - // These are preferences for whether to show the notification bar based on the user's settings - // and they are set in the Settings > Options page in the browser extension. - let disabledAddLoginNotification = false; - let disabledChangedPasswordNotification = false; - - // Look up the active user id from storage - const activeUserIdKey = "activeUserId"; - let activeUserId: string; - chrome.storage.local.get(activeUserIdKey, (obj: any) => { - if (obj == null || obj[activeUserIdKey] == null) { - return; - } - activeUserId = obj[activeUserIdKey]; - }); - - // Look up the user's settings from storage - chrome.storage.local.get(activeUserId, (obj: any) => { - if (obj?.[activeUserId] == null) { - return; - } - - const userSettings = obj[activeUserId].settings; - - // NeverDomains is a dictionary of domains that the user has chosen to never - // show the notification bar on (for login detail collection or password change). - // It is managed in the Settings > Excluded Domains page in the browser extension. - // Example: '{"bitwarden.com":null}' - const excludedDomainsDict = userSettings.neverDomains; - - if ( - excludedDomainsDict != null && - // eslint-disable-next-line - excludedDomainsDict.hasOwnProperty(window.location.hostname) - ) { - return; - } - - // Set local disabled preferences - disabledAddLoginNotification = userSettings.disableAddLoginNotification; - disabledChangedPasswordNotification = userSettings.disableChangedPasswordNotification; - - if (!disabledAddLoginNotification || !disabledChangedPasswordNotification) { - // If the user has not disabled both notifications, then handle the initial page change (null -> actual page) - handlePageChange(); - } - }); - // Message Processing // Listen for messages from the background script diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts new file mode 100644 index 00000000000..d6891325353 --- /dev/null +++ b/apps/browser/src/autofill/types/index.ts @@ -0,0 +1,41 @@ +import { Region } from "@bitwarden/common/platform/abstractions/environment.service"; +import { VaultTimeoutAction } from "@bitwarden/common/src/enums/vault-timeout-action.enum"; + +export type UserSettings = { + avatarColor: string | null; + environmentUrls: { + api: string | null; + base: string | null; + events: string | null; + icons: string | null; + identity: string | null; + keyConnector: string | null; + notifications: string | null; + webVault: string | null; + }; + pinProtected: { [key: string]: any }; + region: Region; + serverConfig: { + environment: { + api: string | null; + cloudRegion: string | null; + identity: string | null; + notifications: string | null; + sso: string | null; + vault: string | null; + }; + featureStates: { [key: string]: any }; + gitHash: string; + server: { [key: string]: any }; + utcDate: string; + version: string; + }; + settings: { + equivalentDomains: string[][]; + }; + neverDomains?: { [key: string]: any }; + disableAddLoginNotification?: boolean; + disableChangedPasswordNotification?: boolean; + vaultTimeout: number; + vaultTimeoutAction: VaultTimeoutAction; +}; From 993f37fbae3b7b011c0fe7235398a1ef85eba5c7 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Tue, 15 Aug 2023 11:35:01 -0400 Subject: [PATCH 014/135] PM-3451 - Update password reprompt logic checks to ensure we have an existing cipher in the add/edit flows to avoid showing the MP reprompt in new cipher scenarios (#6031) --- .../src/app/vault/individual-vault/vault.component.ts | 9 ++++++++- apps/web/src/app/vault/org-vault/vault.component.ts | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 70149cc0026..ba32a27aed8 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -604,7 +604,14 @@ export class VaultComponent implements OnInit, OnDestroy { async editCipherId(id: string) { const cipher = await this.cipherService.get(id); - if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + // if cipher exists (cipher is null when new) and MP reprompt + // is on for this cipher, then show password reprompt + if ( + cipher && + cipher.reprompt !== 0 && + !(await this.passwordRepromptService.showPasswordPrompt()) + ) { + // didn't pass password prompt, so don't open add / edit modal this.go({ cipherId: null, itemId: null }); return; } 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 8370b78cdc3..cfeafbf032c 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -600,7 +600,14 @@ export class VaultComponent implements OnInit, OnDestroy { additionalComponentParameters?: (comp: AddEditComponent) => void ) { const cipher = await this.cipherService.get(cipherId); - if (cipher?.reprompt !== 0 && !(await this.passwordRepromptService.showPasswordPrompt())) { + // if cipher exists (cipher is null when new) and MP reprompt + // is on for this cipher, then show password reprompt + if ( + cipher && + cipher.reprompt !== 0 && + !(await this.passwordRepromptService.showPasswordPrompt()) + ) { + // didn't pass password prompt, so don't open add / edit modal this.go({ cipherId: null, itemId: null }); return; } From b56bb19c023f9ae71490d10063f10adc1812897d Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 15 Aug 2023 19:25:51 +0200 Subject: [PATCH 015/135] [CL-117] Update storybook, re-add addon-designs and add eslint (#6015) --- .eslintrc.json | 3 +- .github/CODEOWNERS | 3 +- .storybook/main.ts | 1 + package-lock.json | 5202 +++++++++++++++++++++++++++++++------------- package.json | 14 +- 5 files changed, 3646 insertions(+), 1577 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index afa134a65ed..f3115898ff8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,7 +20,8 @@ "plugin:import/recommended", "plugin:import/typescript", "prettier", - "plugin:rxjs/recommended" + "plugin:rxjs/recommended", + "plugin:storybook/recommended" ], "settings": { "import/parsers": { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1aed9c1be8c..15de72d785b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -74,6 +74,7 @@ apps/web/src/translation-constants.ts @bitwarden/team-platform-dev apps/browser/src/autofill @bitwarden/team-autofill-dev ## Component Library ## +.storybook @bitwarden/team-platform-dev libs/components @bitwarden/team-platform-dev ## Desktop native module ## @@ -95,7 +96,7 @@ apps/desktop/src/package.json package-lock.json -## Locales ## +## Locales ## apps/browser/src/_locales/en/messages.json apps/cli/src/locales/en/messages.json apps/desktop/src/locales/en/messages.json diff --git a/.storybook/main.ts b/.storybook/main.ts index a7f12f469ba..acc6e2a56db 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -15,6 +15,7 @@ const config: StorybookConfig = { "@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-a11y", + "@storybook/addon-designs", { name: "@storybook/addon-docs", options: { diff --git a/package-lock.json b/package-lock.json index df157a9cff3..b80ec615f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,11 +83,12 @@ "@electron/rebuild": "3.2.13", "@fluffy-spoon/substitute": "1.208.0", "@ngtools/webpack": "15.2.8", - "@storybook/addon-a11y": "7.0.18", - "@storybook/addon-actions": "7.0.18", - "@storybook/addon-essentials": "7.0.18", - "@storybook/addon-links": "7.0.18", - "@storybook/angular": "7.0.18", + "@storybook/addon-a11y": "7.2.2", + "@storybook/addon-actions": "7.2.2", + "@storybook/addon-designs": "7.0.4", + "@storybook/addon-essentials": "7.2.2", + "@storybook/addon-links": "7.2.2", + "@storybook/angular": "7.2.2", "@types/argon2-browser": "1.18.1", "@types/chrome": "0.0.237", "@types/duo_web_sdk": "2.7.1", @@ -136,6 +137,7 @@ "eslint-plugin-import": "2.27.5", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", + "eslint-plugin-storybook": "0.6.13", "eslint-plugin-tailwindcss": "3.12.1", "gulp": "4.0.2", "gulp-filter": "7.0.0", @@ -166,7 +168,7 @@ "rimraf": "5.0.1", "sass": "1.62.1", "sass-loader": "13.3.1", - "storybook": "7.0.18", + "storybook": "7.2.2", "style-loader": "3.3.3", "tailwindcss": "3.3.2", "ts-jest": "29.1.0", @@ -1838,9 +1840,9 @@ "dev": true }, "node_modules/@aw-web-design/x-default-browser": { - "version": "1.4.88", - "resolved": "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.88.tgz", - "integrity": "sha512-AkEmF0wcwYC2QkhK703Y83fxWARttIWXDmQN8+cof8FmFZ5BRhnNXGymeb1S73bOCLfWjYELxtujL56idCN/XA==", + "version": "1.4.126", + "resolved": "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz", + "integrity": "sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==", "dev": true, "dependencies": { "default-browser-id": "3.0.0" @@ -1850,44 +1852,109 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", - "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", - "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.2", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -1898,11 +1965,11 @@ } }, "node_modules/@babel/core/node_modules/@babel/generator": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.9.tgz", - "integrity": "sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dependencies": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -1911,6 +1978,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/core/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/core/node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1925,9 +2005,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1973,58 +2053,86 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz", - "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz", + "integrity": "sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==", "dev": true, "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.22.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", - "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "dependencies": { - "@babel/compat-data": "^7.22.0", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz", - "integrity": "sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz", + "integrity": "sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.22.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.22.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6", - "semver": "^6.3.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2033,24 +2141,48 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz", - "integrity": "sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2059,10 +2191,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -2095,205 +2239,82 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", - "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz", - "integrity": "sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/types": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", - "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dependencies": { - "@babel/types": "^7.21.4" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", - "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/generator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", - "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", - "dependencies": { - "@babel/types": "^7.22.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", - "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", - "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/parser": "^7.21.9", - "@babel/types": "^7.21.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/traverse": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", - "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.22.3", - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.22.4", - "@babel/types": "^7.22.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/types": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", - "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", - "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2302,131 +2323,101 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz", - "integrity": "sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-member-expression-to-functions": "^7.22.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.0" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/generator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", - "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/parser": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", - "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/template": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", - "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/parser": "^7.21.9", - "@babel/types": "^7.21.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/traverse": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", - "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.22.3", - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.22.4", - "@babel/types": "^7.22.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@babel/types": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", - "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", - "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, "dependencies": { - "@babel/types": "^7.20.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2436,6 +2427,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -2444,64 +2436,90 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", - "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz", + "integrity": "sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", - "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -2573,9 +2591,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.9.tgz", - "integrity": "sha512-q5PNg/Bi1OpGgx5jYlvWZwAorZepEudDMCLtj967aeS7WMont7dUZI46M2XwcIQqvUlMxWfdLFu4S/qSxeUu5g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2584,12 +2602,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2599,14 +2617,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2939,12 +2957,12 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", - "integrity": "sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", + "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2954,12 +2972,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -2969,12 +2987,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz", - "integrity": "sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3008,12 +3026,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3125,12 +3143,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3156,12 +3174,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", - "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3171,14 +3189,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz", - "integrity": "sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz", + "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -3206,12 +3224,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3221,12 +3239,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz", + "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3236,13 +3254,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz", - "integrity": "sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3252,13 +3270,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz", - "integrity": "sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -3269,19 +3287,19 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, "engines": { @@ -3291,14 +3309,38 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", - "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/template": "^7.20.7" + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3307,13 +3349,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz", + "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3323,13 +3379,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3339,12 +3395,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3354,12 +3410,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz", - "integrity": "sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -3370,13 +3426,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3386,12 +3442,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz", - "integrity": "sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -3402,13 +3458,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", + "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-flow": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3418,12 +3474,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", - "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3433,14 +3489,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3450,12 +3506,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz", - "integrity": "sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -3466,12 +3522,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3481,12 +3537,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz", - "integrity": "sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -3497,12 +3553,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3512,13 +3568,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3528,13 +3584,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", - "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", "dependencies": { - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-simple-access": "^7.21.5" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3544,15 +3600,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3562,13 +3618,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3578,13 +3634,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3594,12 +3650,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3609,12 +3665,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz", - "integrity": "sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -3625,12 +3681,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz", - "integrity": "sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -3641,16 +3697,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz", - "integrity": "sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.3", - "@babel/helper-compilation-targets": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.3" + "@babel/plugin-transform-parameters": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3660,13 +3716,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3676,12 +3732,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz", - "integrity": "sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -3692,13 +3748,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz", - "integrity": "sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz", + "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -3709,12 +3765,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz", - "integrity": "sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3724,13 +3780,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz", - "integrity": "sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3740,14 +3796,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz", - "integrity": "sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -3757,32 +3813,25 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz", - "integrity": "sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3792,13 +3841,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", - "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "regenerator-transform": "^0.15.1" + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" }, "engines": { "node": ">=6.9.0" @@ -3808,12 +3857,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3852,12 +3901,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3867,13 +3916,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3883,12 +3932,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3898,12 +3947,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3913,12 +3962,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3928,15 +3977,15 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.10.tgz", + "integrity": "sha512-7++c8I/ymsDo4QQBAgbraXLzIM6jmfao11KgIBEYZRReWzNWH9NtNgJcyrZiXsOPh523FQm6LfpLyy/U5fn46A==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.10", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3945,13 +3994,25 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", - "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "node_modules/@babel/plugin-transform-typescript/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3961,13 +4022,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz", - "integrity": "sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3977,13 +4038,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -3993,13 +4054,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz", - "integrity": "sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -4107,14 +4168,14 @@ } }, "node_modules/@babel/preset-flow": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.21.4.tgz", - "integrity": "sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.22.5.tgz", + "integrity": "sha512-ta2qZ+LSiGCrP5pgcGt8xMnnkXQrq8Sa4Ulhy06BOlF5QbLw9q5hIx7bn5MrsvyTGAfh6kTOo07Q+Pfld/8Y5Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-flow-strip-types": "^7.21.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-transform-flow-strip-types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -4140,16 +4201,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", - "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz", + "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-typescript": "^7.21.3" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-typescript": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -4159,9 +4220,9 @@ } }, "node_modules/@babel/register": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.21.0.tgz", - "integrity": "sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.5.tgz", + "integrity": "sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", @@ -4287,9 +4348,9 @@ } }, "node_modules/@babel/register/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -4317,6 +4378,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.20.7", @@ -4327,18 +4389,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", - "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.5", - "@babel/types": "^7.21.5", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -4347,11 +4409,11 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.9.tgz", - "integrity": "sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dependencies": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -4360,6 +4422,17 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -4374,12 +4447,12 @@ } }, "node_modules/@babel/types": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", - "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -4614,72 +4687,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz", - "integrity": "sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-transform-optional-chaining": "^7.22.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz", - "integrity": "sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw==", - "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz", - "integrity": "sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz", - "integrity": "sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env": { "version": "7.22.4", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.4.tgz", @@ -4783,20 +4790,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/types": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", - "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@compodoc/compodoc/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5378,6 +5371,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -5394,6 +5388,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -5410,6 +5405,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -5426,6 +5422,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -5442,6 +5439,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -5458,6 +5456,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -5474,6 +5473,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -5490,6 +5490,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5506,6 +5507,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5522,6 +5524,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5538,6 +5541,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5554,6 +5558,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5570,6 +5575,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5586,6 +5592,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5602,6 +5609,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5618,6 +5626,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -5634,6 +5643,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -5650,6 +5660,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -5666,6 +5677,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=12" } @@ -5682,6 +5694,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -5698,6 +5711,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -5714,6 +5728,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -5847,6 +5862,66 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, + "node_modules/@figspec/components": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.2.tgz", + "integrity": "sha512-rTjjH7wvM55ZuX+MRVPND1cs4Z4JspJvKc9lzGxm/8gD4dLfgeFztQuNy+daGglaxcGXLXTuJ2oJtZ0/lmRKmw==", + "dev": true, + "dependencies": { + "lit": "^2.1.3" + } + }, + "node_modules/@figspec/react": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.3.tgz", + "integrity": "sha512-r683qOko+5CbT48Ox280fMx2MNAtaFPgCNJvldOqN3YtmAzlcTT+YSxd3OahA+kjXGGrnzDbUgeTOX1cPLII+g==", + "dev": true, + "dependencies": { + "@figspec/components": "^1.0.1", + "@lit-labs/react": "^1.0.2" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", + "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "dev": true, + "dependencies": { + "@floating-ui/utils": "^0.1.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", + "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", + "dev": true, + "dependencies": { + "@floating-ui/core": "^1.4.1", + "@floating-ui/utils": "^0.1.1" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz", + "integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==", + "dev": true, + "dependencies": { + "@floating-ui/dom": "^1.3.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", + "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==", + "dev": true + }, "node_modules/@fluffy-spoon/substitute": { "version": "1.208.0", "resolved": "https://registry.npmjs.org/@fluffy-spoon/substitute/-/substitute-1.208.0.tgz", @@ -6596,6 +6671,27 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@lit-labs/react": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.2.1.tgz", + "integrity": "sha512-DiZdJYFU0tBbdQkfwwRSwYyI/mcWkg3sWesKRsHUd4G+NekTmmeq9fzsurvcKTNVa0comNljwtg4Hvi1ds3V+A==", + "dev": true + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.1.tgz", + "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==", + "dev": true + }, + "node_modules/@lit/reactive-element": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", + "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", + "dev": true, + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.0.0" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -6763,9 +6859,9 @@ } }, "node_modules/@ndelangen/get-tarball": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@ndelangen/get-tarball/-/get-tarball-3.0.7.tgz", - "integrity": "sha512-NqGfTZIZpRFef1GoVaShSSRwDC3vde3ThtTeqFdcYd6ipKqnfEVhjK2hUeHjCQUcptyZr2TONqcloFXM+5QBrQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@ndelangen/get-tarball/-/get-tarball-3.0.9.tgz", + "integrity": "sha512-9JKTEik4vq+yGosHYhZ1tiH/3WpUS0Nh0kej4Agndhox8pAdWhEx5knFVRcb/ya9knCRCs1rPxNrSXTDdfVqpA==", "dev": true, "dependencies": { "gunzip-maybe": "^1.4.2", @@ -7096,6 +7192,527 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", + "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", + "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, "node_modules/@schematics/angular": { "version": "15.2.8", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-15.2.8.tgz", @@ -7276,21 +7893,21 @@ } }, "node_modules/@storybook/addon-a11y": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.0.18.tgz", - "integrity": "sha512-kftOzFBpg3goWbLBXDLLsJslfrKYbHs1xtFEH5/4J9NFNE8vZKbIBDQa/R3ezWheL+l2y+5qOlWoLl5z96zlWA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.2.2.tgz", + "integrity": "sha512-gMGP5GdnTENdv5M1+1buk2n9md43jx6S8ZCsXlRagpVM57+jE9WyKtV6R+BPwfX2lBY45nhAj7Qmiy+OrUHarA==", "dev": true, "dependencies": { - "@storybook/addon-highlight": "7.0.18", - "@storybook/channels": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/addon-highlight": "7.2.2", + "@storybook/channels": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "axe-core": "^4.2.0", "lodash": "^4.17.21", "react-resize-detector": "^7.1.2" @@ -7313,19 +7930,19 @@ } }, "node_modules/@storybook/addon-actions": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.0.18.tgz", - "integrity": "sha512-3M5AU/ZD79YP88vKlFezIJbIoG/II7wCixUBTmwiC3BeQZDuVsqPNl8eiP6MGT70xwyx7a993lSM5f5N5W93vg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.2.2.tgz", + "integrity": "sha512-Ev9oGlpxrt7tCpCmAYA04hsZYKnZIiksaQDCgKAf6I2+yUZBBkZh490yBPkMExpH5bLk7fAgjCMZP9gOyzaq3A==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "dequal": "^2.0.2", "lodash": "^4.17.21", "polished": "^4.2.2", @@ -7362,19 +7979,19 @@ } }, "node_modules/@storybook/addon-backgrounds": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.0.18.tgz", - "integrity": "sha512-cPQy1Ot7Urf4hQz+xnF1YKrqSyR0DRwozBmF+sGzceACWmueFl0CifYZC8RSmaiIyVh0RyWPxZ9F/eT67NX2lA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.2.2.tgz", + "integrity": "sha512-ZqSEw9R+ar9jMW6dxfnAAn7hQn9uC9z0om9dbOHNiulOz1CYa62GAyJXU+uZyCyuThqyEa0W8D2+Jsm/s9AuBw==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "memoizerific": "^1.11.3", "ts-dedent": "^2.0.0" }, @@ -7396,20 +8013,21 @@ } }, "node_modules/@storybook/addon-controls": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.0.18.tgz", - "integrity": "sha512-mD6DE52CCMKugXk2Uab0QxwgfE76kFJroxASmnePnXUNWfP9EZJpJXYE3cyyBbmZuxa46VHDGGEGXQWRl4+Eog==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.2.2.tgz", + "integrity": "sha512-kcFG7RMQJVz3fNvQO+9vjAodKbRrobsdr3WJYN6zmugh+qxEdUvHUUe093t0MpMKp2njY6c9eAbfjN+PrLSCJQ==", "dev": true, "dependencies": { - "@storybook/blocks": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-common": "7.0.18", - "@storybook/manager-api": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/blocks": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/core-events": "7.2.2", + "@storybook/manager-api": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "lodash": "^4.17.21", "ts-dedent": "^2.0.0" }, @@ -7430,29 +8048,54 @@ } } }, - "node_modules/@storybook/addon-docs": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.0.18.tgz", - "integrity": "sha512-oq+ZN5809gIRdTZQIpeK1F8BJtL1/VWo9rWvl6ymVOL/Xzdgd7AOfKf9Y99X35RcxAGysRIHLGJjF4bgLoY1Aw==", + "node_modules/@storybook/addon-designs": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@storybook/addon-designs/-/addon-designs-7.0.4.tgz", + "integrity": "sha512-u2Nxa69C6/VuuVI/Uc0vB2Hk+TOt2L/bAatowRBLcVtv7HiDpNuuc7OyW9QJMIzXZ6aVLjI59k53llz7BfExbA==", + "dev": true, + "dependencies": { + "@figspec/react": "^1.0.0" + }, + "peerDependencies": { + "@storybook/addon-docs": "^7.0.0", + "@storybook/addons": "^7.0.0", + "@storybook/components": "^7.0.0", + "@storybook/manager-api": "^7.0.0", + "@storybook/preview-api": "^7.0.0", + "@storybook/theming": "^7.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-docs": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.2.2.tgz", + "integrity": "sha512-rQZ/LAC1oAQityI/NNwntLellAT2yrT7vQAPhHBpaVi/AOdT87wTkCyxnFJj/y3XYDAs4XgUqgbIKSIcRni/eA==", "dev": true, "dependencies": { - "@babel/core": "^7.20.2", - "@babel/plugin-transform-react-jsx": "^7.19.0", "@jest/transform": "^29.3.1", "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/csf-plugin": "7.0.18", - "@storybook/csf-tools": "7.0.18", + "@storybook/blocks": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/csf-plugin": "7.2.2", + "@storybook/csf-tools": "7.2.2", "@storybook/global": "^5.0.0", "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.0.18", - "@storybook/postinstall": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/react-dom-shim": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/node-logger": "7.2.2", + "@storybook/postinstall": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/react-dom-shim": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "fs-extra": "^11.1.0", "remark-external-links": "^8.0.0", "remark-slug": "^6.0.0", @@ -7482,24 +8125,24 @@ } }, "node_modules/@storybook/addon-essentials": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.0.18.tgz", - "integrity": "sha512-0XXu7xhtRefA1WxxorKk6BWeeB+7gQ+r2+bG1zQEfBgDYPR06YbPw4H79IZ8JiR97aJRsZBK5UUhOZMDrc5zcQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.2.2.tgz", + "integrity": "sha512-x99psHZqtHFE7IKnJ3ruZIGe2xkb/xb0IQHya+zmdGHxNkcIeZVCLidPJRjrplJGmykRg+B25q2330CMIUYd9Q==", "dev": true, "dependencies": { - "@storybook/addon-actions": "7.0.18", - "@storybook/addon-backgrounds": "7.0.18", - "@storybook/addon-controls": "7.0.18", - "@storybook/addon-docs": "7.0.18", - "@storybook/addon-highlight": "7.0.18", - "@storybook/addon-measure": "7.0.18", - "@storybook/addon-outline": "7.0.18", - "@storybook/addon-toolbars": "7.0.18", - "@storybook/addon-viewport": "7.0.18", - "@storybook/core-common": "7.0.18", - "@storybook/manager-api": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/preview-api": "7.0.18", + "@storybook/addon-actions": "7.2.2", + "@storybook/addon-backgrounds": "7.2.2", + "@storybook/addon-controls": "7.2.2", + "@storybook/addon-docs": "7.2.2", + "@storybook/addon-highlight": "7.2.2", + "@storybook/addon-measure": "7.2.2", + "@storybook/addon-outline": "7.2.2", + "@storybook/addon-toolbars": "7.2.2", + "@storybook/addon-viewport": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/manager-api": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/preview-api": "7.2.2", "ts-dedent": "^2.0.0" }, "funding": { @@ -7512,14 +8155,14 @@ } }, "node_modules/@storybook/addon-highlight": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.0.18.tgz", - "integrity": "sha512-a3nfUhbu6whoDclIZSV/fzLj132tNNjV05ENTpuN3JpLoMd3+obDUWzeQUs9TetK4RBRN3ewM7sIMEI4oBpgmg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.2.2.tgz", + "integrity": "sha512-jyuxPPTg3q021GYBzA+JoBO2ece6B4gt4ZqtWjKbOpTHpUyzkH6IBcBuWGtgGcQgrCGlmb5B8QCLAb8Cmy6VJg==", "dev": true, "dependencies": { - "@storybook/core-events": "7.0.18", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.0.18" + "@storybook/preview-api": "7.2.2" }, "funding": { "type": "opencollective", @@ -7527,19 +8170,19 @@ } }, "node_modules/@storybook/addon-links": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.0.18.tgz", - "integrity": "sha512-xEwflt7bp9FRoZVeqPGb6d3s2Gh+/jaSmnyIxMxrBy2oovKIqu9ptolqz1AhjFOXfaLs9c2RAmJUuFZJtETLxA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.2.2.tgz", + "integrity": "sha512-bRK0B1QLzpCJ80Cdk/SgA5qlT3RxRkouaJU/rcFc3obHng4bUBr164SLKDgWOHawSrBn3dohO0HIz2o8AevgmA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/router": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/router": "7.2.2", + "@storybook/types": "7.2.2", "prop-types": "^15.7.2", "ts-dedent": "^2.0.0" }, @@ -7561,18 +8204,19 @@ } }, "node_modules/@storybook/addon-measure": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.0.18.tgz", - "integrity": "sha512-iu8vQpGOA+CFYbWR6QNshj20o33OQ/xcTbp5P4U6xGYDUliUBbwJ2KLxcKlmIeBanBrBdz0jPFtHwY4dM1ZdKw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.2.2.tgz", + "integrity": "sha512-gzXj28rqmtHyTKorGleP8+wlvvT6RQMsKtdHCJL/yT3Q9+REQ4/5lAgw+jKqSG+Ka6FxUYWlCN9nD5fRwxtXyQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/types": "7.0.18" + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/types": "7.2.2", + "tiny-invariant": "^1.3.1" }, "funding": { "type": "opencollective", @@ -7592,18 +8236,18 @@ } }, "node_modules/@storybook/addon-outline": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.0.18.tgz", - "integrity": "sha512-3vNWO7ezo6GIvidbz8JxFrKtfVEoTQN7tnZx+wpqmCF8ihBORewkpeMUnvgb9ZKjD0X7gE8eQvvG8KKWcyHDBQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.2.2.tgz", + "integrity": "sha512-Mh6tOiRWVQRgMQlH2GAh2Ov3uf4wmtGXV7MBfKmqxowIp6HGCd4ZJ5tTOgD8ANRFIloH57oSa2/Jf/qiUg0/OQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/types": "7.2.2", "ts-dedent": "^2.0.0" }, "funding": { @@ -7624,16 +8268,16 @@ } }, "node_modules/@storybook/addon-toolbars": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.0.18.tgz", - "integrity": "sha512-mwhq962o0WloHAeFjJ6BXO2nzdTo5KE2fqawPpqcB2lwXP6tvaA2tDWwgntjPCHejqWTS+ZTdO4/1xrMhWYt/g==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.2.2.tgz", + "integrity": "sha512-KQkFwLEqi/Xm9wq6mqaqO6VEW7+Sgbr8BUtVRAB9sSpy2mlhZEzZpcWKjuHmyMb9/eiRvnicM9kXxz0tHAF0jA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18" + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2" }, "funding": { "type": "opencollective", @@ -7653,18 +8297,18 @@ } }, "node_modules/@storybook/addon-viewport": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.0.18.tgz", - "integrity": "sha512-aVVLBsWXfGDX3z1pc93LWWdG5RUoJbGL/JJPMZGwXdwWpP8V3OBl8D8bgPymyg+MgwhSRZZDDGgnJaVGGwZ6bQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.2.2.tgz", + "integrity": "sha512-6qjtkFSV+9Fxw/nS5X7b5xC1kbk+CMGUEZhzmqM+iYtijHZmK0K1+x6AoR/mOul/rLL2YT73U3OYCCKWV9lemQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2", "memoizerific": "^1.11.3", "prop-types": "^15.7.2" }, @@ -7686,14 +8330,14 @@ } }, "node_modules/@storybook/addons": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-7.0.18.tgz", - "integrity": "sha512-+j9ItxWoVzarbllaV4WRaJpDM3P2aC5O6F3cPn4YkG/unb6HOs11WLAqFbzZnLYZNAFvWS8PYEAtqs1BxG66YQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-7.2.2.tgz", + "integrity": "sha512-yWBBpBcRyPP1deAjzWV9OAXrPfeRd/vRpJw09dWHzuD3xtnd3jZ2h+t1r9a5yTSQbP5GO1YdS/WOK5Uf9hcsuw==", "dev": true, "dependencies": { - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/types": "7.0.18" + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/types": "7.2.2" }, "funding": { "type": "opencollective", @@ -7705,26 +8349,25 @@ } }, "node_modules/@storybook/angular": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-7.0.18.tgz", - "integrity": "sha512-dNATvKB4UntYlG65fiCwN86Wu7WOlXTYoTA2I5UoXUMPzREBtSLFAY4XOmsXHGp8PQSAX7voa3vK9fk1yxi8iA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-7.2.2.tgz", + "integrity": "sha512-NXJWXII0rIFTbLZapvaD2Qg/IBe4VQsorbwuiie6/ZBdnCj+DtJwFQ3usVhuPR11woFAvBiWqaxXofsfPmOueQ==", "dev": true, "dependencies": { - "@storybook/builder-webpack5": "7.0.18", - "@storybook/cli": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/core-client": "7.0.18", - "@storybook/core-common": "7.0.18", - "@storybook/core-events": "7.0.18", - "@storybook/core-server": "7.0.18", - "@storybook/core-webpack": "7.0.18", - "@storybook/docs-tools": "7.0.18", + "@storybook/builder-webpack5": "7.2.2", + "@storybook/cli": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/core-events": "7.2.2", + "@storybook/core-server": "7.2.2", + "@storybook/core-webpack": "7.2.2", + "@storybook/docs-tools": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/telemetry": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/telemetry": "7.2.2", + "@storybook/types": "7.2.2", "@types/node": "^16.0.0", "@types/react": "^16.14.34", "@types/react-dom": "^16.9.14", @@ -7735,7 +8378,7 @@ "semver": "^7.3.7", "telejson": "^7.0.3", "ts-dedent": "^2.0.0", - "tsconfig-paths-webpack-plugin": "^3.5.2", + "tsconfig-paths-webpack-plugin": "^4.0.1", "util-deprecate": "^1.0.2", "webpack": "5" }, @@ -7777,60 +8420,23 @@ "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==", "dev": true }, - "node_modules/@storybook/angular/node_modules/tsconfig-paths-webpack-plugin": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.2.tgz", - "integrity": "sha512-EhnfjHbzm5IYI9YPNVIxx1moxMI4bpHD2e0zTXeDNQcwjjRaGepP7IhTHJkyDBG0CAOoxRfe7jCG630Ou+C6Pw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^3.9.0" - } - }, - "node_modules/@storybook/api": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-7.0.18.tgz", - "integrity": "sha512-gikVJBR2z7LdepljmbvbsrYgywQm3jNEEEmjG0OwYDeYNjWPuoQSffT+LoyouaaCK90d1osJLl3062OkwlIG8g==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/manager-api": "7.0.18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/@storybook/blocks": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.0.18.tgz", - "integrity": "sha512-HLsuzmUdVIeFXEP5v5vyjnEePRNYjzltwTjCKQhHAlt8/aQZmREiIMOfoMoAa1Rd+On8Ib2DUd2cN10VS18H8A==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.2.2.tgz", + "integrity": "sha512-VgO46E5zA8oo/Cn8kT9o3xiFtnqxlqsRRGp5t8A1YqgN2OvYTtg5/PLS16XH8Qui/m9EvOoT7DlOmcqlp3Z78w==", "dev": true, "dependencies": { - "@storybook/channels": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/channels": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/csf": "^0.1.0", - "@storybook/docs-tools": "7.0.18", + "@storybook/docs-tools": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -7840,6 +8446,7 @@ "polished": "^4.2.2", "react-colorful": "^5.1.2", "telejson": "^7.0.3", + "tocbot": "^4.20.1", "ts-dedent": "^2.0.0", "util-deprecate": "^1.0.2" }, @@ -7853,21 +8460,21 @@ } }, "node_modules/@storybook/builder-manager": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.0.18.tgz", - "integrity": "sha512-yFMm3xuYkyg2hS1uz3CkvyvLzK7qJsDPVEh7lew8GiJK1Xx8cc+FnAOlRTjWNxvhfiT296wAMCTPWv7LeoSgqQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.2.2.tgz", + "integrity": "sha512-19LqtL5/Yos9/wXqTxg+zOOK6M312eHXAdaYt2REGk1iqJzQXoy4wnmE2rbjBMmD5bUTzWm2vkmHGtkzAjwzzA==", "dev": true, "dependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", - "@storybook/core-common": "7.0.18", - "@storybook/manager": "7.0.18", - "@storybook/node-logger": "7.0.18", + "@storybook/core-common": "7.2.2", + "@storybook/manager": "7.2.2", + "@storybook/node-logger": "7.2.2", "@types/ejs": "^3.1.1", "@types/find-cache-dir": "^3.2.1", "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", "browser-assert": "^1.2.1", "ejs": "^3.1.8", - "esbuild": "^0.17.0", + "esbuild": "^0.18.0", "esbuild-plugin-alias": "^0.2.1", "express": "^4.17.3", "find-cache-dir": "^3.0.0", @@ -7880,6 +8487,395 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/builder-manager/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "node_modules/@storybook/builder-manager/node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -7895,54 +8891,55 @@ } }, "node_modules/@storybook/builder-webpack5": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-7.0.18.tgz", - "integrity": "sha512-ciDOHrnChHWjikQwsM+xGz70PGfWurcezCyRPPRY0zimyHWtlug6V1Q9dJAdEAFsxqFSZA/qg7gEcZyqdlTMaA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-7.2.2.tgz", + "integrity": "sha512-+f2H3tXPZfn3q/eruXCmWPVAopd1MMEAAvneZ4iqlSHPC5HK2dcX/zj3yPar6rjfHHvZBXNm5sSi4WuD5Pw9MA==", "dev": true, "dependencies": { - "@babel/core": "^7.12.10", - "@storybook/addons": "7.0.18", - "@storybook/api": "7.0.18", - "@storybook/channel-postmessage": "7.0.18", - "@storybook/channel-websocket": "7.0.18", - "@storybook/channels": "7.0.18", - "@storybook/client-api": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/components": "7.0.18", - "@storybook/core-common": "7.0.18", - "@storybook/core-events": "7.0.18", - "@storybook/core-webpack": "7.0.18", + "@babel/core": "^7.22.9", + "@storybook/addons": "7.2.2", + "@storybook/channels": "7.2.2", + "@storybook/client-api": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/components": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/core-events": "7.2.2", + "@storybook/core-webpack": "7.2.2", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/preview": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/router": "7.0.18", - "@storybook/store": "7.0.18", - "@storybook/theming": "7.0.18", + "@storybook/manager-api": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/preview": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/router": "7.2.2", + "@storybook/store": "7.2.2", + "@storybook/theming": "7.2.2", + "@swc/core": "^1.3.49", "@types/node": "^16.0.0", "@types/semver": "^7.3.4", "babel-loader": "^9.0.0", "babel-plugin-named-exports-order": "^0.0.2", "browser-assert": "^1.2.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", + "constants-browserify": "^1.0.0", "css-loader": "^6.7.1", "express": "^4.17.3", - "fork-ts-checker-webpack-plugin": "^7.2.8", + "fork-ts-checker-webpack-plugin": "^8.0.0", "fs-extra": "^11.1.0", "html-webpack-plugin": "^5.5.0", "path-browserify": "^1.0.1", "process": "^0.11.10", "semver": "^7.3.7", "style-loader": "^3.3.1", + "swc-loader": "^0.2.3", "terser-webpack-plugin": "^5.3.1", "ts-dedent": "^2.0.0", + "url": "^0.11.0", "util": "^0.12.4", "util-deprecate": "^1.0.2", "webpack": "5", - "webpack-dev-middleware": "^5.3.1", + "webpack-dev-middleware": "^6.1.1", "webpack-hot-middleware": "^2.25.1", - "webpack-virtual-modules": "^0.4.3" + "webpack-virtual-modules": "^0.5.0" }, "funding": { "type": "opencollective", @@ -7959,9 +8956,9 @@ } }, "node_modules/@storybook/builder-webpack5/node_modules/@types/node": { - "version": "16.18.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.33.tgz", - "integrity": "sha512-WjW7iYRVtePnSeLxVfE1e+g1yStJrfR9Anuv4y6JZVgOqYyFcW7GhPBk2/J1d0rC4ZNLrI13lS4e32NUuuRmHA==", + "version": "16.18.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", "dev": true }, "node_modules/@storybook/builder-webpack5/node_modules/fs-extra": { @@ -7979,90 +8976,71 @@ } }, "node_modules/@storybook/builder-webpack5/node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.1.tgz", + "integrity": "sha512-y51HrHaFeeWir0YO4f0g+9GwZawuigzcAdRNon6jErXy/SqV/+O6eaVAzDqE6t3e3NpGeR5CS+cCDaTC+V3yEQ==", "dev": true, "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.4.3", + "memfs": "^3.4.12", "mime-types": "^2.1.31", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/@storybook/channel-postmessage": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-7.0.18.tgz", - "integrity": "sha512-rpwBH5ANdPnugS6+7xG9qHSoS+aPSEnBxDKsONWFubfMTTXQuFkf/793rBbxGkoINdqh8kSdKOM2rIty6e9cmQ==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/core-events": "7.0.18", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.0.3" + "webpack": "^5.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/channel-websocket": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-7.0.18.tgz", - "integrity": "sha512-QYsZIfe23NN4i+oIdPKHaYBehk3a/HYk57a+M2oR3Frmv8IOqc/e31uH+xx5NxnjHrTJj7Y80ZJw6EKB682S6w==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/global": "^5.0.0", - "telejson": "^7.0.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "peerDependenciesMeta": { + "webpack": { + "optional": true + } } }, "node_modules/@storybook/channels": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.0.18.tgz", - "integrity": "sha512-rkA7ea0M3+dWS+71iHJdiZ5R2QuIdiVg0CgyLJHDagc1qej7pEVNhMWtppeq+X5Pwp9nkz8ZTQ7aCjTf6th0/A==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.2.2.tgz", + "integrity": "sha512-FkH5QzKkq7smtPlaKTWalJ+sN13H4dWtxdZ+ePFAXaubsBqGqo3Dw3e/hrbjrMqFjTwiKnmj5K5bjhdJcvzi1A==", "dev": true, + "dependencies": { + "@storybook/client-logger": "7.2.2", + "@storybook/core-events": "7.2.2", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.0.3", + "tiny-invariant": "^1.3.1" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, "node_modules/@storybook/cli": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.0.18.tgz", - "integrity": "sha512-9n4J4thiCUsGSXiRc6ZysqYUaCMCrpu0/qgC+5ngfFRuMmZgUV0y5+0fmaOhT2XjsonTTgucizO82i7+ottCVg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.2.2.tgz", + "integrity": "sha512-YoXRCRICj4NEqUvYvgVRmk5IJadou6feCymI5r94z7XHQiObMoZd3p6QjyEp8ayi8XpxoJM/Hrddu3hddxUvyw==", "dev": true, "dependencies": { - "@babel/core": "^7.20.2", - "@babel/preset-env": "^7.20.2", + "@babel/core": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/types": "^7.22.5", "@ndelangen/get-tarball": "^3.0.7", - "@storybook/codemod": "7.0.18", - "@storybook/core-common": "7.0.18", - "@storybook/core-server": "7.0.18", - "@storybook/csf-tools": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/telemetry": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/codemod": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/core-server": "7.2.2", + "@storybook/csf-tools": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/telemetry": "7.2.2", + "@storybook/types": "7.2.2", "@types/semver": "^7.3.4", - "boxen": "^5.1.2", + "@yarnpkg/fslib": "2.10.3", + "@yarnpkg/libzip": "2.3.0", "chalk": "^4.1.0", "commander": "^6.2.1", "cross-spawn": "^7.0.3", @@ -8084,8 +9062,7 @@ "puppeteer-core": "^2.1.1", "read-pkg-up": "^7.0.1", "semver": "^7.3.7", - "shelljs": "^0.8.5", - "simple-update-notifier": "^1.0.0", + "simple-update-notifier": "^2.0.0", "strip-json-comments": "^3.0.1", "tempy": "^1.0.1", "ts-dedent": "^2.0.0", @@ -8100,6 +9077,216 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/cli/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/cli/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/cli/node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/cli/node_modules/@babel/preset-env": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.10.tgz", + "integrity": "sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.10", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.10", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.10", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.10", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.10", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/cli/node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@storybook/cli/node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/cli/node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/cli/node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@storybook/cli/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.31.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/cli/node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@storybook/cli/node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -8123,14 +9310,59 @@ "node": ">=14.14" } }, - "node_modules/@storybook/client-api": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-7.0.18.tgz", - "integrity": "sha512-EdgE4om6nXZf/sDZcVMGMeKv4BPX+P3EKUfMHCHjlrbbeL0eeY8Ynf+u/wYrIYZPUodS8TEV5XchHVB8F7rLBQ==", + "node_modules/@storybook/cli/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/preview-api": "7.0.18" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/cli/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/cli/node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/cli/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@storybook/client-api": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-7.2.2.tgz", + "integrity": "sha512-47WDNZ5FdvtQuSijg3IexxFve2aBrBih6VHdKKcel2SSiS0W01043fNMsP1T+m6kJr4jdnp0J5GVQpTQFi8Kxg==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "7.2.2", + "@storybook/preview-api": "7.2.2" }, "funding": { "type": "opencollective", @@ -8138,9 +9370,9 @@ } }, "node_modules/@storybook/client-logger": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.0.18.tgz", - "integrity": "sha512-uKgFdVedYoRDZBVrE1IBdWNHDFln1IxWEeI+7ZiNSQwREG9swHpU5Fa8DceclM/oLjJRuzG1jFzv+XZY8894+Q==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.2.2.tgz", + "integrity": "sha512-ULqPNTJsJdlWTQt5V/hEv4CUq7GgrLzLvcjhKB9IYbp4a0gjhinfq7jBFIcXRE8BSOQLui2PDGE3SzElzOp5/g==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -8151,18 +9383,19 @@ } }, "node_modules/@storybook/codemod": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.0.18.tgz", - "integrity": "sha512-+9XFns29e8FpPLsqA8ZCQ3mNnIIKD3QnqGYkbkCVKi/G1fomvVQsIfsnkrYv5SobTbz29B4aNWxAaeSnO7/OGg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.2.2.tgz", + "integrity": "sha512-i8WY3PJeJalVWTjLChlJREYHp42mGw0GXLqTH/q0hbAwzuVBBTrsMqy+oXOWRCAeCGbK5uP10GOLN23s+zuNTQ==", "dev": true, "dependencies": { - "@babel/core": "~7.21.0", - "@babel/preset-env": "~7.21.0", - "@babel/types": "~7.21.2", + "@babel/core": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/types": "^7.22.5", "@storybook/csf": "^0.1.0", - "@storybook/csf-tools": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/csf-tools": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/types": "7.2.2", + "@types/cross-spawn": "^6.0.2", "cross-spawn": "^7.0.3", "globby": "^11.0.2", "jscodeshift": "^0.14.0", @@ -8175,39 +9408,71 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/codemod/node_modules/@babel/preset-env": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz", - "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==", + "node_modules/@storybook/codemod/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", - "@babel/plugin-proposal-async-generator-functions": "^7.20.7", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.21.0", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.21.0", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/codemod/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/codemod/node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/codemod/node_modules/@babel/preset-env": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.10.tgz", + "integrity": "sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -8218,45 +9483,62 @@ "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.20.11", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.21.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.21.5", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.10", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.10", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.10", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.10", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.10", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -8265,26 +9547,81 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@storybook/codemod/node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/codemod/node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/codemod/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.31.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@storybook/codemod/node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@storybook/codemod/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@storybook/components": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.0.18.tgz", - "integrity": "sha512-Jn1CbF9UAKt8BVaZtuhmthpcZ02VMaCFXR0ISfDXCpiMKnylmpP0+WfXcoKLzz6yS+EW8EW5S9+Qq8xgQY8H7A==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.2.2.tgz", + "integrity": "sha512-A2SavROGKFK76w4pfMGr0Zf3A2QaQrnJqPZOfVB6dKrXBwgwM3VhdUj6N8cI9X4W7VaEvEyUWXVTCv+bG+yZtQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", + "@radix-ui/react-select": "^1.2.2", + "@storybook/client-logger": "7.2.2", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/icons": "^1.1.0", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "memoizerific": "^1.11.3", "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" @@ -8298,40 +9635,29 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/core-client": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.0.18.tgz", - "integrity": "sha512-ueExRZx6fd9LRssgdhDJ0bL4Ir2RrbXzJz/kjIT2KgYY3l7jkhe0dpT3bOgGKjQt0f7XMFU24t/r7aDLGMB+2Q==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/preview-api": "7.0.18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, "node_modules/@storybook/core-common": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.0.18.tgz", - "integrity": "sha512-HZAB1NIK/Yv0x9poyzqYcue2tx39+MAF1mbHgGy+JJZRerO2fRShgo8f8VPH9ChbFCoJ7isL5wNhgGdg9kp2kA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.2.2.tgz", + "integrity": "sha512-evLV0oRLhByHVhrncRXdSmQL5VP8jLXUOBPiM63oCnSWDsw4GMt37JrmCYBNM4E1YiJ7zSllmvcFCBmyChcPFA==", "dev": true, "dependencies": { - "@storybook/node-logger": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/node-logger": "7.2.2", + "@storybook/types": "7.2.2", + "@types/find-cache-dir": "^3.2.1", "@types/node": "^16.0.0", + "@types/node-fetch": "^2.6.4", "@types/pretty-hrtime": "^1.0.0", "chalk": "^4.1.0", - "esbuild": "^0.17.0", + "esbuild": "^0.18.0", "esbuild-register": "^3.4.0", - "file-system-cache": "^2.0.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", "find-up": "^5.0.0", "fs-extra": "^11.1.0", - "glob": "^8.1.0", - "glob-promise": "^6.0.2", + "glob": "^10.0.0", "handlebars": "^4.7.7", "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", "picomatch": "^2.3.0", "pkg-dir": "^5.0.0", "pretty-hrtime": "^1.0.3", @@ -8343,12 +9669,410 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/core-common/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/core-common/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@storybook/core-common/node_modules/@types/node": { - "version": "16.18.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.34.tgz", - "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==", + "version": "16.18.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", "dev": true }, + "node_modules/@storybook/core-common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@storybook/core-common/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "node_modules/@storybook/core-common/node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -8363,10 +10087,56 @@ "node": ">=14.14" } }, + "node_modules/@storybook/core-common/node_modules/glob": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@storybook/core-common/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@storybook/core-common/node_modules/minipass": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", + "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/@storybook/core-events": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.0.18.tgz", - "integrity": "sha512-7gxHBQDezdKOeq/u1LL80Bwjfcwsv7XOS3yWQElcgqp+gLaYB6OwwgtkCB2yV6a6l4nep9IdPWE8G3TxIzn9xw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.2.2.tgz", + "integrity": "sha512-0MUsOygFSyYRIWHrVAA7Y7zBoehdILgK2AbnV42qescmPD48YyovkSRiGq0BwSWvuvMRq+094dp7sh2tkfSGHg==", "dev": true, "funding": { "type": "opencollective", @@ -8374,32 +10144,31 @@ } }, "node_modules/@storybook/core-server": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.0.18.tgz", - "integrity": "sha512-zGSGYSoCaSXM28OYKW7zsmpo8VU1icubXLRgdF21fbMhFN1WVS+bPA5+gSkAMf8acq5RNM8uSKskh7E2YDVEqA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.2.2.tgz", + "integrity": "sha512-djtZIe/dFvjppuUMuTSHq51NW4dgkKAercfHwyyzcvN3m+ZBZdUENCUzBfwsS0+Pn64lVgppg6otm8ckMFilMQ==", "dev": true, "dependencies": { - "@aw-web-design/x-default-browser": "1.4.88", + "@aw-web-design/x-default-browser": "1.4.126", "@discoveryjs/json-ext": "^0.5.3", - "@storybook/builder-manager": "7.0.18", - "@storybook/core-common": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/builder-manager": "7.2.2", + "@storybook/channels": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/csf": "^0.1.0", - "@storybook/csf-tools": "7.0.18", + "@storybook/csf-tools": "7.2.2", "@storybook/docs-mdx": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/manager": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/telemetry": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/manager": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/telemetry": "7.2.2", + "@storybook/types": "7.2.2", "@types/detect-port": "^1.3.0", "@types/node": "^16.0.0", - "@types/node-fetch": "^2.5.7", "@types/pretty-hrtime": "^1.0.0", "@types/semver": "^7.3.4", - "better-opn": "^2.1.1", - "boxen": "^5.1.2", + "better-opn": "^3.0.2", "chalk": "^4.1.0", "cli-table3": "^0.6.1", "compression": "^1.7.4", @@ -8409,7 +10178,6 @@ "globby": "^11.0.2", "ip": "^2.0.0", "lodash": "^4.17.21", - "node-fetch": "^2.6.7", "open": "^8.4.0", "pretty-hrtime": "^1.0.3", "prompts": "^2.4.0", @@ -8417,7 +10185,9 @@ "semver": "^7.3.7", "serve-favicon": "^2.5.0", "telejson": "^7.0.3", + "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0", + "util": "^0.12.4", "util-deprecate": "^1.0.2", "watchpack": "^2.2.0", "ws": "^8.2.3" @@ -8428,9 +10198,9 @@ } }, "node_modules/@storybook/core-server/node_modules/@types/node": { - "version": "16.18.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.34.tgz", - "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==", + "version": "16.18.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", "dev": true }, "node_modules/@storybook/core-server/node_modules/fs-extra": { @@ -8469,14 +10239,14 @@ } }, "node_modules/@storybook/core-webpack": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-7.0.18.tgz", - "integrity": "sha512-U5e1r8cgZZzd/Lw9StIrACMVINCvucKm8ZfcFpPh0bjEv4+2qjo9tL3dLNh4OwKznvbzSE6pEO6cBjaphjTe1A==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-7.2.2.tgz", + "integrity": "sha512-KwdY+mFTWclIJM+qF25u0d4gQ/951uOZkgrsiRheKI5BKwD9omuF2OfLexWKnMEWz+UHzHKoN0QFlxcKs/rddw==", "dev": true, "dependencies": { - "@storybook/core-common": "7.0.18", - "@storybook/node-logger": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/core-common": "7.2.2", + "@storybook/node-logger": "7.2.2", + "@storybook/types": "7.2.2", "@types/node": "^16.0.0", "ts-dedent": "^2.0.0" }, @@ -8486,9 +10256,9 @@ } }, "node_modules/@storybook/core-webpack/node_modules/@types/node": { - "version": "16.18.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.34.tgz", - "integrity": "sha512-VmVm7gXwhkUimRfBwVI1CHhwp86jDWR04B5FGebMMyxV90SlCmFujwUHrxTD4oO+SOYU86SoxvhgeRQJY7iXFg==", + "version": "16.18.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", "dev": true }, "node_modules/@storybook/csf": { @@ -8501,13 +10271,13 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.0.18.tgz", - "integrity": "sha512-Cr/Qr4/H4JIYgbbmDjQIYuqjp6nOaZga73R3KZcuClk27B90sI2ADegMYvORgbFgSkwweNQjgak6hLoOyogAhw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.2.2.tgz", + "integrity": "sha512-8iHeK0zfcCIXbcwQm89Bj6Ejqak/dXBwQUMFmgmKzJ4VdyzKdhLgRO3T3EYGsX4AvH0dnuAPONP5uVrGEF8iLw==", "dev": true, "dependencies": { - "@storybook/csf-tools": "7.0.18", - "unplugin": "^0.10.2" + "@storybook/csf-tools": "7.2.2", + "unplugin": "^1.3.1" }, "funding": { "type": "opencollective", @@ -8515,17 +10285,17 @@ } }, "node_modules/@storybook/csf-tools": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.0.18.tgz", - "integrity": "sha512-0IJ2qdrxleTl67FUzsEvGcy96CY0OKyERE33tAsLNbvWcabdJKpLHP+rJwbsCw4z6IlS+kkmEffeFf5qRPTwkQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.2.2.tgz", + "integrity": "sha512-uO9AXtc64tpEpnoqHk5eYCUUL6nMwo1xHTXgIst9NKOdNSBL1/wvRgaT+rW+AzyXGsBjMwvDNjX/HIptqsoNgw==", "dev": true, "dependencies": { - "@babel/generator": "~7.21.1", - "@babel/parser": "~7.21.2", - "@babel/traverse": "~7.21.2", - "@babel/types": "~7.21.2", + "@babel/generator": "^7.22.9", + "@babel/parser": "^7.22.7", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", "@storybook/csf": "^0.1.0", - "@storybook/types": "7.0.18", + "@storybook/types": "7.2.2", "fs-extra": "^11.1.0", "recast": "^0.23.1", "ts-dedent": "^2.0.0" @@ -8536,12 +10306,12 @@ } }, "node_modules/@storybook/csf-tools/node_modules/@babel/generator": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.9.tgz", - "integrity": "sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dev": true, "dependencies": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -8585,15 +10355,14 @@ "dev": true }, "node_modules/@storybook/docs-tools": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.0.18.tgz", - "integrity": "sha512-H95dW2DquGQ75ZVrFjvznPdCxT0eW6esDnemzLJB61KitcYZrWRavfrZzFtUcpzIa84OgY5pllFYt636v11LHQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.2.2.tgz", + "integrity": "sha512-57PiramTgJio0J8HyKQI8cjb/5pTFvKxWBji8UdM6WUe1EqLIwZbymUcOQZDQWr3H+6bdrm4pgYxj7XglcUa5A==", "dev": true, "dependencies": { - "@babel/core": "^7.12.10", - "@storybook/core-common": "7.0.18", - "@storybook/preview-api": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/core-common": "7.2.2", + "@storybook/preview-api": "7.2.2", + "@storybook/types": "7.2.2", "@types/doctrine": "^0.0.3", "doctrine": "^3.0.0", "lodash": "^4.17.21" @@ -8609,10 +10378,23 @@ "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", "dev": true }, + "node_modules/@storybook/icons": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.1.6.tgz", + "integrity": "sha512-co5gDCYPojRAc5lRMnWxbjrR1V37/rTmAo9Vok4a1hDpHZIwkGTWesdzvYivSQXYFxZTpxdM1b5K3W87brnahw==", + "dev": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@storybook/manager": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.0.18.tgz", - "integrity": "sha512-hasb8XDmkT9lyX2cwb3Xg0ngcNQ1QCNHKurl2YJtXowb1CvawGKokhnVUTso15NCnurolDyw/Wqka1sagfm+Mg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.2.2.tgz", + "integrity": "sha512-a7pGZMj5r5vk39IfB7Ca32DNkSTM6SEp+BZhqc4jMDHwSEsGKOZ+GZtWJipUlB1fEnHtdA3LH6IKTKbcpYP+Rg==", "dev": true, "funding": { "type": "opencollective", @@ -8620,19 +10402,19 @@ } }, "node_modules/@storybook/manager-api": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.0.18.tgz", - "integrity": "sha512-anQkm09twL96YkKGXHa+LI0+yMaY6Jxs1lRaetHdMlIqN4VHBHhizHaMgtGfH6xCTuO3WdrKTN7cZii5RH7PBQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.2.2.tgz", + "integrity": "sha512-7EI7TABGGB3VOTc4q7byC5dW/9A1xUJyR1gfCPU+7XiSNItnCz+seBJMSaf6Em/9wYxSAL6PQAGhrwTHGzgWAA==", "dev": true, "dependencies": { - "@storybook/channels": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/channels": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/router": "7.0.18", - "@storybook/theming": "7.0.18", - "@storybook/types": "7.0.18", + "@storybook/router": "7.2.2", + "@storybook/theming": "7.2.2", + "@storybook/types": "7.2.2", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", @@ -8657,25 +10439,19 @@ "dev": true }, "node_modules/@storybook/node-logger": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.0.18.tgz", - "integrity": "sha512-cIeKEBvELtoVP/5UeQ01GJWZ7wM69/9Q+R5uOtNQBlwWFcCD6AVFWMRqq7ObMvdJG/okhXSF+sDetb+BF3zvdw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.2.2.tgz", + "integrity": "sha512-Pn6NJ349S+6tkzaFc1j3qOniDR9DyFPRfZsnAT85APKQaXyzNB3b1xwAhxnVjiCHIfp/a+SLOAdfXa+F8ALFQQ==", "dev": true, - "dependencies": { - "@types/npmlog": "^4.1.2", - "chalk": "^4.1.0", - "npmlog": "^5.0.1", - "pretty-hrtime": "^1.0.3" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, "node_modules/@storybook/postinstall": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.0.18.tgz", - "integrity": "sha512-ObIwAK2UiYhXN/7UifISQgBoH5jnyxh6T8kvCw83YhC78SDOPNgIGjToJECizJ7iubtqAWtCfCT5TrGEpyLGbg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.2.2.tgz", + "integrity": "sha512-tGFZAFu5QFnuzhmzGMpasETVpzD1CVskok4+Dns/3iBWb8fZsFWaC0ZKazO+vHBtWZYO4uvrUbFSDTX2yPhtTQ==", "dev": true, "funding": { "type": "opencollective", @@ -8683,9 +10459,9 @@ } }, "node_modules/@storybook/preview": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.0.18.tgz", - "integrity": "sha512-L53p2eo8G12U6tp7hD3mk5tdWFXLvdEyV9e7a1x9bw1LfH15K/bp8lO6U/W1kkpse7+rqWBqoTjJC1Ktm5Sxog==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.2.2.tgz", + "integrity": "sha512-F3S3yK+RmpriADWnfVZsRN36WRT6LaFjD0sNrUkX8duxdnxNDaLFfJ3Cbxwyv/2lZ48uByIQbX2LC5HieVI0KA==", "dev": true, "funding": { "type": "opencollective", @@ -8693,18 +10469,17 @@ } }, "node_modules/@storybook/preview-api": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.0.18.tgz", - "integrity": "sha512-xxtC0gPGMn/DbwvS4ZuJaBwfFNsjUCf0yLYHFrNe6fxncbvcLZ550RuyUwYuIRfsiKrlgfa3QmmCa4JM/JesHQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.2.2.tgz", + "integrity": "sha512-II0EioQCGS2zTSoHbXNKyI1rwk2X7MBi2/tJerj4w4Qwi2fDQlwM0LKsIWlRjXTxBpOAsOoTelh24wSBoZu4bg==", "dev": true, "dependencies": { - "@storybook/channel-postmessage": "7.0.18", - "@storybook/channels": "7.0.18", - "@storybook/client-logger": "7.0.18", - "@storybook/core-events": "7.0.18", + "@storybook/channels": "7.2.2", + "@storybook/client-logger": "7.2.2", + "@storybook/core-events": "7.2.2", "@storybook/csf": "^0.1.0", "@storybook/global": "^5.0.0", - "@storybook/types": "7.0.18", + "@storybook/types": "7.2.2", "@types/qs": "^6.9.5", "dequal": "^2.0.2", "lodash": "^4.17.21", @@ -8720,9 +10495,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.0.18.tgz", - "integrity": "sha512-O1FRypR8q1katjbznnxI+NtALd2gaWa7KnTwbIDf+ddZltXHMZ8xMiEGEtAMrfXlIuqIr9UvmLRfKZC/ysuA+g==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.2.2.tgz", + "integrity": "sha512-H/yHlWl94vbUv1NNolcB3vCy1S185XKicswljJRC9o/AFCJK+a7d9wDVGTPKF6oy7ujrgpANLQyq02d+OuoO7w==", "dev": true, "funding": { "type": "opencollective", @@ -8734,12 +10509,12 @@ } }, "node_modules/@storybook/router": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.0.18.tgz", - "integrity": "sha512-Mue4s/BnKgdYcsiW9yuvW3qL9k3AgYn5HIhnkBExAteyiUGdAca4IJFhArmGgFktgeLc4ecBQ7sgaCljApnbgg==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.2.2.tgz", + "integrity": "sha512-cnJg43dm3dVifKkRBUsQ4wXC4sJOm46JAS95yRPeGACoHpJTcbCWk1n5GLYA7ozO+KFQSNdxHxPIjNqvnzMFiA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", + "@storybook/client-logger": "7.2.2", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, @@ -8753,13 +10528,13 @@ } }, "node_modules/@storybook/store": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/store/-/store-7.0.18.tgz", - "integrity": "sha512-rvQOG7R1+r77Y9jwNqQB3EKW6D5kzIGoxqzFHd1oDqeY5+vqPXHC/J5iDrl8TZ4GES7ZMAHpkTySbY+rRQK7Ng==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/store/-/store-7.2.2.tgz", + "integrity": "sha512-4Z0iFdD6u9u9rDvr0ekLXOx1tIcmS1OU3xhk92RFWbGpAx4+bb1JDWmO7T04l+uPvLvYpWn5NrSV1ZdgyTVxUg==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/preview-api": "7.0.18" + "@storybook/client-logger": "7.2.2", + "@storybook/preview-api": "7.2.2" }, "funding": { "type": "opencollective", @@ -8767,19 +10542,18 @@ } }, "node_modules/@storybook/telemetry": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.0.18.tgz", - "integrity": "sha512-JP5Z7lGU+oKjNmz2cZW5J7EerwyWBBPOU+NvvooZsymIx02ZvJ4ClmFtolJnBM7m4KoAy50JxV5NQWi+q8PicQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.2.2.tgz", + "integrity": "sha512-pJ8oQ++QD7hLezARp+3PR0JAw3FH6cmOrSs4KAB+IhNgACs8gehaEdy7TAikor5tlAUCWvqPetnNXDrrDIZhTQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.0.18", - "@storybook/core-common": "7.0.18", + "@storybook/client-logger": "7.2.2", + "@storybook/core-common": "7.2.2", + "@storybook/csf-tools": "7.2.2", "chalk": "^4.1.0", "detect-package-manager": "^2.0.1", "fetch-retry": "^5.0.2", "fs-extra": "^11.1.0", - "isomorphic-unfetch": "^3.1.0", - "nanoid": "^3.3.1", "read-pkg-up": "^7.0.1" }, "funding": { @@ -8802,13 +10576,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.0.18.tgz", - "integrity": "sha512-P1gMKa/mKQHIMq0sxBIwTzAcF6v/6hrc62YmkuV62vXu+8zNV2YWbRwywqm3Q6faZEadmb/bL9+z8whaKhCL/g==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.2.2.tgz", + "integrity": "sha512-B4nxcxl4IyVvB1NXwRi4yopAS73zl052f2zJi3kVghJbZ3tgPwgvi3CVpOs2D4pgmxOrKCgiLnzLrGTH+13+0A==", "dev": true, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.0.18", + "@storybook/client-logger": "7.2.2", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -8822,21 +10596,215 @@ } }, "node_modules/@storybook/types": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.0.18.tgz", - "integrity": "sha512-qPop2CbvmX42/BX29YT9jIzW2TlMcMjAE+KCpcKLBiD1oT5DJ1fhMzpe6RW9HkMegkBxjWx54iamN4oHM/pwcQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.2.2.tgz", + "integrity": "sha512-yrL0+KD+dsusQvDmNKQGv36WjvdhoiUxMDEBgsZkP047kRc3b8/zEbD3Tu7iMDsWnpgwip1Frgy29Ro3UtK57Q==", "dev": true, "dependencies": { - "@storybook/channels": "7.0.18", + "@storybook/channels": "7.2.2", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", - "file-system-cache": "^2.0.0" + "file-system-cache": "2.3.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, + "node_modules/@swc/core": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.76.tgz", + "integrity": "sha512-aYYTA2aVYkwJAZepQXtPnkUthhOfn8qd6rsh+lrJxonFrjmpI7RHt2tMDVTXP6XDX7fvnvrVtT1bwZfmBFPh0Q==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.76", + "@swc/core-darwin-x64": "1.3.76", + "@swc/core-linux-arm-gnueabihf": "1.3.76", + "@swc/core-linux-arm64-gnu": "1.3.76", + "@swc/core-linux-arm64-musl": "1.3.76", + "@swc/core-linux-x64-gnu": "1.3.76", + "@swc/core-linux-x64-musl": "1.3.76", + "@swc/core-win32-arm64-msvc": "1.3.76", + "@swc/core-win32-ia32-msvc": "1.3.76", + "@swc/core-win32-x64-msvc": "1.3.76" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.76.tgz", + "integrity": "sha512-ovviEhZ/1E81Z9OGrO0ivLWk4VCa3I3ZzM+cd3gugglRRwVwtlIaoIYqY5S3KiCAupDd1+UCl5X7Vbio7a/V8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.76.tgz", + "integrity": "sha512-tcySTDqs0SHCebtW35sCdcLWsmTEo7bEwx0gNL/spetqVT9fpFi6qU8qcnt7i2KaZHbeNl9g1aadu+Yrni+GzA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.76.tgz", + "integrity": "sha512-apgzpGWy1AwoMF4urAAASsAjE7rEzZFIF+p6utuxhS7cNHzE0AyEVDYJbo+pzBdlZ8orBdzzsHtFwoEgKOjebA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.76.tgz", + "integrity": "sha512-c3c0zz6S0eludqidDpuqbadE0WT3OZczyQxe9Vw8lFFXES85mvNGtwYzyGK2o7TICpsuHrndwDIoYpmpWk879g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.76.tgz", + "integrity": "sha512-Is3bpq7F2qtlnkzEeOD6HIZJPpOmu3q6c82lKww90Q0NnrlSluVMozTHJgwVoFZyizH7uLnk0LuNcEAWLnmJIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.76.tgz", + "integrity": "sha512-iwCeRzd9oSvUzqt7nU6p/ztceAWfnO9XVxBn502R5gs6QCBbE1HCKrWHDO77aKPK7ss+0NcIGHvXTd9L8/wRzw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.76.tgz", + "integrity": "sha512-a671g4tW8kyFeuICsgq4uB9ukQfiIyXJT4V6YSnmqhCTz5mazWuDxZ5wKnx/1g5nXTl+U5cWH2TZaCJatp4GKA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.76.tgz", + "integrity": "sha512-+swEFtjdMezS0vKUhJC3psdSDtOJGY5pEOt4e8XOPvn7aQpKQ9LfF49XVtIwDSk5SGuWtVoLFzkSY3reWUJCyg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.76.tgz", + "integrity": "sha512-5CqwAykpGBJ3PqGLOlWGLGIPpBAG1IwWVDUfro3hhjQ7XJxV5Z1aQf5V5OJ90HJVtrEAVx2xx59UV/Dh081LOg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.76", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.76.tgz", + "integrity": "sha512-CiMpWLLlR3Cew9067E7XxaLBwYYJ90r9EhGSO6V1pvYSWj7ET/Ppmtj1ZhzPJMqRXAP6xflfl5R5o4ee1m4WLA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -9088,6 +11056,15 @@ "@types/node": "*" } }, + "node_modules/@types/cross-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.2.tgz", + "integrity": "sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", @@ -9098,9 +11075,9 @@ } }, "node_modules/@types/detect-port": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.3.2.tgz", - "integrity": "sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.3.3.tgz", + "integrity": "sha512-bV/jQlAJ/nPY3XqSatkGpu+nGzou+uSwrH1cROhn+jBFg47yaNH+blW4C7p9KhopC7QxCv/6M86s37k8dMk0Yg==", "dev": true }, "node_modules/@types/doctrine": { @@ -9460,9 +11437,9 @@ } }, "node_modules/@types/mdx": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.5.tgz", - "integrity": "sha512-76CqzuD6Q7LC+AtbPqrvD9AqsN0k8bsYo2bM2J8pmNldP1aIPAbzUQ7QbobyXL4eLr1wK5x8FZFe8eF/ubRuBg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.6.tgz", + "integrity": "sha512-sVcwEG10aFU2KcM7cIA0M410UPv/DesOPyG8zMVk0QUDexHA3lYmGucpEpZ2dtWWhi2ip3CG+5g/iH0PwoW4Fw==", "dev": true }, "node_modules/@types/mime": { @@ -9543,12 +11520,6 @@ "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "dev": true }, - "node_modules/@types/npmlog": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.4.tgz", - "integrity": "sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==", - "dev": true - }, "node_modules/@types/papaparse": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.7.tgz", @@ -9733,6 +11704,12 @@ "integrity": "sha512-ONpcZAEYlbPx4EtJwfTyCDQJGUpKf4sEcuySdCVjK5Fj/3vHp5HII1fqa1/+qrsLnpYELCQTfVW/awsGJePoIg==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", + "dev": true + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -10919,9 +12896,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -11128,15 +13105,6 @@ "node": ">=0.4.2" } }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "dependencies": { - "string-width": "^4.1.0" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -11420,6 +13388,18 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", + "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "dev": true, + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -12299,31 +14279,15 @@ } }, "node_modules/better-opn": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-2.1.1.tgz", - "integrity": "sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", + "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", "dev": true, "dependencies": { - "open": "^7.0.3" + "open": "^8.0.4" }, "engines": { - "node": ">8.0.0" - } - }, - "node_modules/better-opn/node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.0.0" } }, "node_modules/big-integer": { @@ -12579,52 +14543,6 @@ "popper.js": "^1.16.1" } }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -13177,9 +15095,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001489", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz", - "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==", + "version": "1.0.30001519", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz", + "integrity": "sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==", "funding": [ { "type": "opencollective", @@ -13640,18 +15558,6 @@ "rimraf": "bin.js" } }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -14179,6 +16085,12 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", + "dev": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -14363,18 +16275,50 @@ } }, "node_modules/core-js-compat": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", - "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.0.tgz", + "integrity": "sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw==", "dev": true, "dependencies": { - "browserslist": "^4.21.5" + "browserslist": "^4.21.9" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, + "node_modules/core-js-compat/node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -15300,6 +17244,12 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "dev": true + }, "node_modules/detect-package-manager": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz", @@ -15644,12 +17594,15 @@ } }, "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "dev": true, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, "node_modules/dotenv-expand": { @@ -15934,9 +17887,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.408", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.408.tgz", - "integrity": "sha512-vjeaj0u/UYnzA/CIdGXzzcxRLCqRwREYc9YfaWInjIEr7/XPttZ6ShpyqapchEy0S2r6LpLjDBTnNj7ZxnxJKg==" + "version": "1.4.490", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz", + "integrity": "sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==" }, "node_modules/electron-updater": { "version": "5.3.0", @@ -16751,6 +18704,33 @@ "typescript": ">=4.0.0" } }, + "node_modules/eslint-plugin-storybook": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.13.tgz", + "integrity": "sha512-smd+CS0WH1jBqUEJ3znGS7DU4ayBE9z6lkQAK2yrSUv1+rq8BT/tiI5C/rKE7rmiqiAfojtNYZRhzo5HrulccQ==", + "dev": true, + "dependencies": { + "@storybook/csf": "^0.0.1", + "@typescript-eslint/utils": "^5.45.0", + "requireindex": "^1.1.0", + "ts-dedent": "^2.2.0" + }, + "engines": { + "node": "12.x || 14.x || >= 16" + }, + "peerDependencies": { + "eslint": ">=6" + } + }, + "node_modules/eslint-plugin-storybook/node_modules/@storybook/csf": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz", + "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/eslint-plugin-tailwindcss": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.12.1.tgz", @@ -18197,9 +20177,9 @@ "dev": true }, "node_modules/flow-parser": { - "version": "0.207.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.207.0.tgz", - "integrity": "sha512-s90OlXqzWj1xc4yUtqD1Gr8pGVx0/5rk9gsqPrOYF1kBAPMH4opkmzdWgQ8aNe3Pckqtwr8DlYGbfE2GnW+zsg==", + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.214.0.tgz", + "integrity": "sha512-RW1Dh6BuT14DA7+gtNRKzgzvG3GTPdrceHCi4ddZ9VFGQ9HtO5L8wzxMGsor7XtInIrbWZZCSak0oxnBF7tApw==", "dev": true, "engines": { "node": ">=0.4.0" @@ -18300,9 +20280,9 @@ "dev": true }, "node_modules/fork-ts-checker-webpack-plugin": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.3.0.tgz", - "integrity": "sha512-IN+XTzusCjR5VgntYFgxbxVx3WraPRnKehBFrf00cMSrtUuW9MsG9dhL6MWpY6MkjC3wVwoujfCDgZZCQwbswA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", + "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", @@ -18324,13 +20304,7 @@ }, "peerDependencies": { "typescript": ">3.6.0", - "vue-template-compiler": "*", "webpack": "^5.11.0" - }, - "peerDependenciesMeta": { - "vue-template-compiler": { - "optional": true - } } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { @@ -18365,9 +20339,9 @@ "dev": true }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -18638,6 +20612,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/get-npm-tarball-url": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/get-npm-tarball-url/-/get-npm-tarball-url-2.0.3.tgz", @@ -18775,35 +20758,6 @@ "node": ">= 6" } }, - "node_modules/glob-promise": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-6.0.2.tgz", - "integrity": "sha512-Ni2aDyD1ekD6x8/+K4hDriRDbzzfuK4yKpqSymJ4P7IxbtARiOOuU+k40kbHM0sLIlbf1Qh0qdMkAHMZYE6XJQ==", - "dev": true, - "dependencies": { - "@types/glob": "^8.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/ahmadnassri" - }, - "peerDependencies": { - "glob": "^8.0.3" - } - }, - "node_modules/glob-promise/node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, "node_modules/glob-stream": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", @@ -20883,6 +22837,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -21697,16 +23660,6 @@ "node": ">=0.10.0" } }, - "node_modules/isomorphic-unfetch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", - "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", - "dev": true, - "dependencies": { - "node-fetch": "^2.6.1", - "unfetch": "^4.2.0" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -23875,6 +25828,37 @@ } } }, + "node_modules/lit": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", + "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==", + "dev": true, + "dependencies": { + "@lit/reactive-element": "^1.6.0", + "lit-element": "^3.3.0", + "lit-html": "^2.8.0" + } + }, + "node_modules/lit-element": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", + "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", + "dev": true, + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.1.0", + "@lit/reactive-element": "^1.3.0", + "lit-html": "^2.8.0" + } + }, + "node_modules/lit-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", + "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", + "dev": true, + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", @@ -24556,9 +26540,9 @@ } }, "node_modules/markdown-to-jsx": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.2.1.tgz", - "integrity": "sha512-9HrdzBAo0+sFz9ZYAGT5fB8ilzTW+q6lPocRxrIesMO+aB40V9MgFfbfMXxlGjf22OpRy+IXlvVaQenicdpgbg==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.3.2.tgz", + "integrity": "sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==", "dev": true, "engines": { "node": ">= 10" @@ -26429,9 +28413,9 @@ } }, "node_modules/node-fetch-native": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.1.1.tgz", - "integrity": "sha512-9VvspTSUp2Sxbl+9vbZTlFGq9lHwE8GDVVekxx6YsNd1YH59sb3Ba8v3Y3cD8PkLNcileGGcA21PFjVl0jzDaw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.2.0.tgz", + "integrity": "sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==", "dev": true }, "node_modules/node-fetch/node_modules/tr46": { @@ -26627,9 +28611,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "node_modules/node.extend": { "version": "2.0.2", @@ -27918,13 +29902,13 @@ } }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -27966,9 +29950,9 @@ } }, "node_modules/pathe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", - "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", "dev": true }, "node_modules/pause-stream": { @@ -29523,6 +31507,53 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dev": true, + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", + "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", + "dev": true, + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-resize-detector": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-7.1.2.tgz", @@ -29536,6 +31567,29 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dev": true, + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -29990,9 +32044,9 @@ "dev": true }, "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" @@ -31360,43 +33414,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -32548,12 +34565,12 @@ "dev": true }, "node_modules/storybook": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.0.18.tgz", - "integrity": "sha512-FXMmTiomSlLPTHty7vGLr0prPf6pCV07EwAmNOYYYTskitEYV0R7hlhawByd7HuobjIhHvSTKesa1Whl86zLNA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.2.2.tgz", + "integrity": "sha512-JT4CtVagLi7B5CKFUX/ozRpW3X7z4ffdXaXr4g6uBQjPN8NmvQCCftVIntpiXPKIbQa+cqyOTCNiprGvFnQQfg==", "dev": true, "dependencies": { - "@storybook/cli": "7.0.18" + "@storybook/cli": "7.2.2" }, "bin": { "sb": "index.js", @@ -32976,6 +34993,16 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/swc-loader": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.3.tgz", + "integrity": "sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A==", + "dev": true, + "peerDependencies": { + "@swc/core": "^1.2.147", + "webpack": ">=2" + } + }, "node_modules/sweetalert2": { "version": "10.16.11", "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-10.16.11.tgz", @@ -33625,6 +35652,12 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "dev": true }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "dev": true + }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -33825,6 +35858,12 @@ "xtend": "~4.0.1" } }, + "node_modules/tocbot": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.21.1.tgz", + "integrity": "sha512-IfajhBTeg0HlMXu1f+VMbPef05QpDTsZ9X2Yn1+8npdaXsXg/+wrm9Ze1WG5OS1UDC3qJ5EQN/XOZ3gfXjPFCw==", + "dev": true + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -34397,12 +36436,6 @@ "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", "dev": true }, - "node_modules/unfetch": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", - "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", - "dev": true - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -34654,15 +36687,15 @@ } }, "node_modules/unplugin": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-0.10.2.tgz", - "integrity": "sha512-6rk7GUa4ICYjae5PrAllvcDeuT8pA9+j5J5EkxbMFaV+SalHhxZ7X2dohMzu6C3XzsMT+6jwR/+pwPNR3uK9MA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.4.0.tgz", + "integrity": "sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "chokidar": "^3.5.3", "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.4.5" + "webpack-virtual-modules": "^0.5.0" } }, "node_modules/unset-value": { @@ -34817,6 +36850,27 @@ "node": ">=0.10.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", + "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-resize-observer": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", @@ -34830,6 +36884,28 @@ "react-dom": "16.8.0 - 18" } }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dev": true, + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -35508,9 +37584,9 @@ } }, "node_modules/webpack-hot-middleware": { - "version": "2.25.3", - "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.3.tgz", - "integrity": "sha512-IK/0WAHs7MTu1tzLTjio73LjS3Ov+VvBKQmE8WPlJutgG5zT6Urgq/BbAdRrHTRpyzK0dvAvFh1Qg98akxgZpA==", + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz", + "integrity": "sha512-IRmTspuHM06aZh98OhBJtqLpeWFM8FXJS5UYpKYxCJzyFoyWj1w6VGFfomZU7OPA55dMLrQK0pRT1eQ3PACr4w==", "dev": true, "dependencies": { "ansi-html-community": "0.0.8", @@ -35571,9 +37647,9 @@ } }, "node_modules/webpack-virtual-modules": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz", - "integrity": "sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz", + "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==", "dev": true }, "node_modules/webpack/node_modules/ajv": { @@ -35781,18 +37857,6 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", diff --git a/package.json b/package.json index cc0d47e39f4..115de695349 100644 --- a/package.json +++ b/package.json @@ -46,11 +46,12 @@ "@electron/rebuild": "3.2.13", "@fluffy-spoon/substitute": "1.208.0", "@ngtools/webpack": "15.2.8", - "@storybook/addon-a11y": "7.0.18", - "@storybook/addon-actions": "7.0.18", - "@storybook/addon-essentials": "7.0.18", - "@storybook/addon-links": "7.0.18", - "@storybook/angular": "7.0.18", + "@storybook/addon-a11y": "7.2.2", + "@storybook/addon-actions": "7.2.2", + "@storybook/addon-designs": "7.0.4", + "@storybook/addon-essentials": "7.2.2", + "@storybook/addon-links": "7.2.2", + "@storybook/angular": "7.2.2", "@types/argon2-browser": "1.18.1", "@types/chrome": "0.0.237", "@types/duo_web_sdk": "2.7.1", @@ -99,6 +100,7 @@ "eslint-plugin-import": "2.27.5", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", + "eslint-plugin-storybook": "0.6.13", "eslint-plugin-tailwindcss": "3.12.1", "gulp": "4.0.2", "gulp-filter": "7.0.0", @@ -129,7 +131,7 @@ "rimraf": "5.0.1", "sass": "1.62.1", "sass-loader": "13.3.1", - "storybook": "7.0.18", + "storybook": "7.2.2", "style-loader": "3.3.3", "tailwindcss": "3.3.2", "ts-jest": "29.1.0", From 15f29c5fb15d02a09fd543859be0e6721a86d6c4 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Tue, 15 Aug 2023 20:32:40 +0200 Subject: [PATCH 016/135] [PM-3040] [BEEEP] Extend json-export to include passwordhistory and vault item dates (created, updated, deleted) (#5917) * Add password history to json exports Change callout to not mention missing password history any longer * Added item meta dates to json exports Added vault items creation-/revision-/deleted-dates to json exports * Removed unnecessary promises * Add bitwarden-json-export types Define types Use types in vault-export-service Move existing password-protected type to export-types * Use bitwarden-json-export types in bitwarden-json-importer * Clean up passwordHistory if needed * Define and use bitwarden-csv-export-types --- apps/web/src/locales/en/messages.json | 4 +- .../export-scope-callout.component.ts | 2 +- .../common/src/models/export/cipher.export.ts | 35 +++++ .../models/export/password-history.export.ts | 40 +++++ .../vault-export/bitwarden-csv-export-type.ts | 23 +++ .../bitwarden-json-export-types.ts | 51 +++++++ .../bitwarden-password-protected-types.ts | 11 -- .../services/vault-export.service.ts | 37 +++-- libs/importer/src/importers/base-importer.ts | 3 + .../bitwarden/bitwarden-json-importer.ts | 139 +++++++++++------- .../bitwarden-password-protected-importer.ts | 2 +- 11 files changed, 264 insertions(+), 83 deletions(-) create mode 100644 libs/common/src/models/export/password-history.export.ts create mode 100644 libs/exporter/src/vault-export/bitwarden-csv-export-type.ts create mode 100644 libs/exporter/src/vault-export/bitwarden-json-export-types.ts delete mode 100644 libs/exporter/src/vault-export/bitwarden-password-protected-types.ts diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index e15b3f1b17e..97ad4adb408 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -5454,8 +5454,8 @@ "exportingOrganizationVaultTitle": { "message": "Exporting organization vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated password history or attachments.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/libs/angular/src/tools/export/components/export-scope-callout.component.ts b/libs/angular/src/tools/export/components/export-scope-callout.component.ts index 3095cd5e835..a97db1079a7 100644 --- a/libs/angular/src/tools/export/components/export-scope-callout.component.ts +++ b/libs/angular/src/tools/export/components/export-scope-callout.component.ts @@ -35,7 +35,7 @@ export class ExportScopeCalloutComponent implements OnInit { } : { title: "exportingPersonalVaultTitle", - description: "exportingPersonalVaultDescription", + description: "exportingIndividualVaultDescription", scopeIdentifier: await this.stateService.getEmail(), }; this.show = true; diff --git a/libs/common/src/models/export/cipher.export.ts b/libs/common/src/models/export/cipher.export.ts index 5bfde78b557..342cf59fd86 100644 --- a/libs/common/src/models/export/cipher.export.ts +++ b/libs/common/src/models/export/cipher.export.ts @@ -8,6 +8,7 @@ import { CardExport } from "./card.export"; import { FieldExport } from "./field.export"; import { IdentityExport } from "./identity.export"; import { LoginExport } from "./login.export"; +import { PasswordHistoryExport } from "./password-history.export"; import { SecureNoteExport } from "./secure-note.export"; export class CipherExport { @@ -26,6 +27,10 @@ export class CipherExport { req.card = null; req.identity = null; req.reprompt = CipherRepromptType.None; + req.passwordHistory = []; + req.creationDate = null; + req.revisionDate = null; + req.deletedDate = null; return req; } @@ -63,6 +68,13 @@ export class CipherExport { break; } + if (req.passwordHistory != null) { + view.passwordHistory = req.passwordHistory.map((ph) => PasswordHistoryExport.toView(ph)); + } + + view.creationDate = req.creationDate; + view.revisionDate = req.revisionDate; + view.deletedDate = req.deletedDate; return view; } @@ -96,6 +108,13 @@ export class CipherExport { break; } + if (req.passwordHistory != null) { + domain.passwordHistory = req.passwordHistory.map((ph) => PasswordHistoryExport.toDomain(ph)); + } + + domain.creationDate = req.creationDate; + domain.revisionDate = req.revisionDate; + domain.deletedDate = req.deletedDate; return domain; } @@ -112,6 +131,10 @@ export class CipherExport { card: CardExport; identity: IdentityExport; reprompt: CipherRepromptType; + passwordHistory: PasswordHistoryExport[] = null; + revisionDate: Date = null; + creationDate: Date = null; + deletedDate: Date = null; // Use build method instead of ctor so that we can control order of JSON stringify for pretty print build(o: CipherView | CipherDomain) { @@ -152,5 +175,17 @@ export class CipherExport { this.identity = new IdentityExport(o.identity); break; } + + if (o.passwordHistory != null) { + if (o instanceof CipherView) { + this.passwordHistory = o.passwordHistory.map((ph) => new PasswordHistoryExport(ph)); + } else { + this.passwordHistory = o.passwordHistory.map((ph) => new PasswordHistoryExport(ph)); + } + } + + this.creationDate = o.creationDate; + this.revisionDate = o.revisionDate; + this.deletedDate = o.deletedDate; } } diff --git a/libs/common/src/models/export/password-history.export.ts b/libs/common/src/models/export/password-history.export.ts new file mode 100644 index 00000000000..0bdbc6697ac --- /dev/null +++ b/libs/common/src/models/export/password-history.export.ts @@ -0,0 +1,40 @@ +import { EncString } from "../../platform/models/domain/enc-string"; +import { Password } from "../../vault/models/domain/password"; +import { PasswordHistoryView } from "../../vault/models/view/password-history.view"; + +export class PasswordHistoryExport { + static template(): PasswordHistoryExport { + const req = new PasswordHistoryExport(); + req.password = null; + req.lastUsedDate = null; + return req; + } + + static toView(req: PasswordHistoryExport, view = new PasswordHistoryView()) { + view.password = req.password; + view.lastUsedDate = req.lastUsedDate; + return view; + } + + static toDomain(req: PasswordHistoryExport, domain = new Password()) { + domain.password = req.password != null ? new EncString(req.password) : null; + domain.lastUsedDate = req.lastUsedDate; + return domain; + } + + password: string; + lastUsedDate: Date = null; + + constructor(o?: PasswordHistoryView | Password) { + if (o == null) { + return; + } + + if (o instanceof PasswordHistoryView) { + this.password = o.password; + } else { + this.password = o.password?.encryptedString; + } + this.lastUsedDate = o.lastUsedDate; + } +} diff --git a/libs/exporter/src/vault-export/bitwarden-csv-export-type.ts b/libs/exporter/src/vault-export/bitwarden-csv-export-type.ts new file mode 100644 index 00000000000..30c6bb89bc1 --- /dev/null +++ b/libs/exporter/src/vault-export/bitwarden-csv-export-type.ts @@ -0,0 +1,23 @@ +import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; + +export type BitwardenCsvExportType = { + type: string; + name: string; + notes: string; + fields: string; + reprompt: CipherRepromptType; + // Login props + login_uri: string[]; + login_username: string; + login_password: string; + login_totp: string; + favorite: number | null; +}; + +export type BitwardenCsvIndividualExportType = BitwardenCsvExportType & { + folder: string | null; +}; + +export type BitwardenCsvOrgExportType = BitwardenCsvExportType & { + collections: string[] | null; +}; diff --git a/libs/exporter/src/vault-export/bitwarden-json-export-types.ts b/libs/exporter/src/vault-export/bitwarden-json-export-types.ts new file mode 100644 index 00000000000..ab2bcbb9f1f --- /dev/null +++ b/libs/exporter/src/vault-export/bitwarden-json-export-types.ts @@ -0,0 +1,51 @@ +import { + CipherWithIdExport, + CollectionWithIdExport, + FolderWithIdExport, +} from "@bitwarden/common/models/export"; + +// Base +export type BitwardenJsonExport = { + encrypted: boolean; + items: CipherWithIdExport[]; +}; + +// Decrypted +export type BitwardenUnEncryptedJsonExport = BitwardenJsonExport & { + encrypted: false; +}; + +export type BitwardenUnEncryptedIndividualJsonExport = BitwardenUnEncryptedJsonExport & { + folders: FolderWithIdExport[]; +}; + +export type BitwardenUnEncryptedOrgJsonExport = BitwardenUnEncryptedJsonExport & { + collections: CollectionWithIdExport[]; +}; + +// Account-encrypted +export type BitwardenEncryptedJsonExport = BitwardenJsonExport & { + encrypted: true; + encKeyValidation_DO_NOT_EDIT: string; +}; + +export type BitwardenEncryptedIndividualJsonExport = BitwardenEncryptedJsonExport & { + folders: FolderWithIdExport[]; +}; + +export type BitwardenEncryptedOrgJsonExport = BitwardenEncryptedJsonExport & { + collections: CollectionWithIdExport[]; +}; + +// Password-protected +export type BitwardenPasswordProtectedFileFormat = { + encrypted: boolean; + passwordProtected: boolean; + salt: string; + kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; + kdfType: number; + encKeyValidation_DO_NOT_EDIT: string; + data: string; +}; diff --git a/libs/exporter/src/vault-export/bitwarden-password-protected-types.ts b/libs/exporter/src/vault-export/bitwarden-password-protected-types.ts deleted file mode 100644 index 01671c16804..00000000000 --- a/libs/exporter/src/vault-export/bitwarden-password-protected-types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface BitwardenPasswordProtectedFileFormat { - encrypted: boolean; - passwordProtected: boolean; - salt: string; - kdfIterations: number; - kdfMemory?: number; - kdfParallelism?: number; - kdfType: number; - encKeyValidation_DO_NOT_EDIT: string; - data: string; -} diff --git a/libs/exporter/src/vault-export/services/vault-export.service.ts b/libs/exporter/src/vault-export/services/vault-export.service.ts index 06a8b45f58a..8609ce0d1c8 100644 --- a/libs/exporter/src/vault-export/services/vault-export.service.ts +++ b/libs/exporter/src/vault-export/services/vault-export.service.ts @@ -26,7 +26,18 @@ import { CollectionView } from "@bitwarden/common/vault/models/view/collection.v import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { ExportHelper } from "../../export-helper"; -import { BitwardenPasswordProtectedFileFormat } from "../bitwarden-password-protected-types"; +import { + BitwardenCsvExportType, + BitwardenCsvIndividualExportType, + BitwardenCsvOrgExportType, +} from "../bitwarden-csv-export-type"; +import { + BitwardenEncryptedIndividualJsonExport, + BitwardenEncryptedOrgJsonExport, + BitwardenUnEncryptedIndividualJsonExport, + BitwardenUnEncryptedOrgJsonExport, + BitwardenPasswordProtectedFileFormat, +} from "../bitwarden-json-export-types"; import { ExportFormat, VaultExportServiceAbstraction } from "./vault-export.service.abstraction"; @@ -123,7 +134,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { } }); - const exportCiphers: any[] = []; + const exportCiphers: BitwardenCsvIndividualExportType[] = []; decCiphers.forEach((c) => { // only export logins and secure notes if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) { @@ -133,7 +144,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { return; } - const cipher: any = {}; + const cipher = {} as BitwardenCsvIndividualExportType; cipher.folder = c.folderId != null && foldersMap.has(c.folderId) ? foldersMap.get(c.folderId).name : null; cipher.favorite = c.favorite ? 1 : null; @@ -143,7 +154,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { return papa.unparse(exportCiphers); } else { - const jsonDoc: any = { + const jsonDoc: BitwardenUnEncryptedIndividualJsonExport = { encrypted: false, folders: [], items: [], @@ -193,7 +204,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid()); - const jsonDoc: any = { + const jsonDoc: BitwardenEncryptedIndividualJsonExport = { encrypted: true, encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, folders: [], @@ -269,14 +280,14 @@ export class VaultExportService implements VaultExportServiceAbstraction { collectionsMap.set(c.id, c); }); - const exportCiphers: any[] = []; + const exportCiphers: BitwardenCsvOrgExportType[] = []; decCiphers.forEach((c) => { // only export logins and secure notes if (c.type !== CipherType.Login && c.type !== CipherType.SecureNote) { return; } - const cipher: any = {}; + const cipher = {} as BitwardenCsvOrgExportType; cipher.collections = []; if (c.collectionIds != null) { cipher.collections = c.collectionIds @@ -289,7 +300,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { return papa.unparse(exportCiphers); } else { - const jsonDoc: any = { + const jsonDoc: BitwardenUnEncryptedOrgJsonExport = { encrypted: false, collections: [], items: [], @@ -317,20 +328,17 @@ export class VaultExportService implements VaultExportServiceAbstraction { promises.push( this.apiService.getCollections(organizationId).then((c) => { - const collectionPromises: any = []; if (c != null && c.data != null && c.data.length > 0) { c.data.forEach((r) => { const collection = new Collection(new CollectionData(r as CollectionDetailsResponse)); collections.push(collection); }); } - return Promise.all(collectionPromises); }) ); promises.push( this.apiService.getCiphersOrganization(organizationId).then((c) => { - const cipherPromises: any = []; if (c != null && c.data != null && c.data.length > 0) { c.data .filter((item) => item.deletedDate === null) @@ -339,7 +347,6 @@ export class VaultExportService implements VaultExportServiceAbstraction { ciphers.push(cipher); }); } - return Promise.all(cipherPromises); }) ); @@ -348,7 +355,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { const orgKey = await this.cryptoService.getOrgKey(organizationId); const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), orgKey); - const jsonDoc: any = { + const jsonDoc: BitwardenEncryptedOrgJsonExport = { encrypted: true, encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, collections: [], @@ -369,7 +376,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { return JSON.stringify(jsonDoc, null, " "); } - private buildCommonCipher(cipher: any, c: CipherView) { + private buildCommonCipher(cipher: BitwardenCsvExportType, c: CipherView): BitwardenCsvExportType { cipher.type = null; cipher.name = c.name; cipher.notes = c.notes; @@ -382,7 +389,7 @@ export class VaultExportService implements VaultExportServiceAbstraction { cipher.login_totp = null; if (c.fields) { - c.fields.forEach((f: any) => { + c.fields.forEach((f) => { if (!cipher.fields) { cipher.fields = ""; } else { diff --git a/libs/importer/src/importers/base-importer.ts b/libs/importer/src/importers/base-importer.ts index 7c3110ad232..676fec4f333 100644 --- a/libs/importer/src/importers/base-importer.ts +++ b/libs/importer/src/importers/base-importer.ts @@ -313,6 +313,9 @@ export abstract class BaseImporter { if (cipher.fields != null && cipher.fields.length === 0) { cipher.fields = null; } + if (cipher.passwordHistory != null && cipher.passwordHistory.length === 0) { + cipher.passwordHistory = null; + } } protected processKvp( diff --git a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts index 0789c7df2e4..5c281dc6b73 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts @@ -6,13 +6,21 @@ import { import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; +import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { + BitwardenEncryptedIndividualJsonExport, + BitwardenEncryptedOrgJsonExport, + BitwardenJsonExport, + BitwardenUnEncryptedIndividualJsonExport, + BitwardenUnEncryptedOrgJsonExport, +} from "@bitwarden/exporter/vault-export/bitwarden-json-export-types"; import { ImportResult } from "../../models/import-result"; import { BaseImporter } from "../base-importer"; import { Importer } from "../importer"; export class BitwardenJsonImporter extends BaseImporter implements Importer { - private results: any; private result: ImportResult; protected constructor( @@ -24,25 +32,27 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { async parse(data: string): Promise { this.result = new ImportResult(); - this.results = JSON.parse(data); - if (this.results == null || this.results.items == null) { + const results: BitwardenJsonExport = JSON.parse(data); + if (results == null || results.items == null) { this.result.success = false; return this.result; } - if (this.results.encrypted) { - await this.parseEncrypted(); + if (results.encrypted) { + await this.parseEncrypted(results as any); } else { - this.parseDecrypted(); + await this.parseDecrypted(results as any); } return this.result; } - private async parseEncrypted() { - if (this.results.encKeyValidation_DO_NOT_EDIT != null) { + private async parseEncrypted( + results: BitwardenEncryptedIndividualJsonExport | BitwardenEncryptedOrgJsonExport + ) { + if (results.encKeyValidation_DO_NOT_EDIT != null) { const orgKey = await this.cryptoService.getOrgKey(this.organizationId); - const encKeyValidation = new EncString(this.results.encKeyValidation_DO_NOT_EDIT); + const encKeyValidation = new EncString(results.encKeyValidation_DO_NOT_EDIT); const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8( encKeyValidation, orgKey @@ -54,30 +64,11 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { } } - const groupingsMap = new Map(); + const groupingsMap = this.organization + ? await this.parseCollections(results as BitwardenEncryptedOrgJsonExport) + : await this.parseFolders(results as BitwardenEncryptedIndividualJsonExport); - if (this.organization && this.results.collections != null) { - for (const c of this.results.collections as CollectionWithIdExport[]) { - const collection = CollectionWithIdExport.toDomain(c); - if (collection != null) { - collection.organizationId = this.organizationId; - const view = await collection.decrypt(); - groupingsMap.set(c.id, this.result.collections.length); - this.result.collections.push(view); - } - } - } else if (!this.organization && this.results.folders != null) { - for (const f of this.results.folders as FolderWithIdExport[]) { - const folder = FolderWithIdExport.toDomain(f); - if (folder != null) { - const view = await folder.decrypt(); - groupingsMap.set(f.id, this.result.folders.length); - this.result.folders.push(view); - } - } - } - - for (const c of this.results.items as CipherWithIdExport[]) { + for (const c of results.items) { const cipher = CipherWithIdExport.toDomain(c); // reset ids incase they were set for some reason cipher.id = null; @@ -113,28 +104,14 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { this.result.success = true; } - private parseDecrypted() { - const groupingsMap = new Map(); - if (this.organization && this.results.collections != null) { - this.results.collections.forEach((c: CollectionWithIdExport) => { - const collection = CollectionWithIdExport.toView(c); - if (collection != null) { - collection.organizationId = null; - groupingsMap.set(c.id, this.result.collections.length); - this.result.collections.push(collection); - } - }); - } else if (!this.organization && this.results.folders != null) { - this.results.folders.forEach((f: FolderWithIdExport) => { - const folder = FolderWithIdExport.toView(f); - if (folder != null) { - groupingsMap.set(f.id, this.result.folders.length); - this.result.folders.push(folder); - } - }); - } + private async parseDecrypted( + results: BitwardenUnEncryptedIndividualJsonExport | BitwardenUnEncryptedOrgJsonExport + ) { + const groupingsMap = this.organization + ? await this.parseCollections(results as BitwardenUnEncryptedOrgJsonExport) + : await this.parseFolders(results as BitwardenUnEncryptedIndividualJsonExport); - this.results.items.forEach((c: CipherWithIdExport) => { + results.items.forEach((c) => { const cipher = CipherWithIdExport.toView(c); // reset ids incase they were set for some reason cipher.id = null; @@ -168,4 +145,60 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { this.result.success = true; } + + private async parseFolders( + data: BitwardenUnEncryptedIndividualJsonExport | BitwardenEncryptedIndividualJsonExport + ): Promise> | null { + if (data.folders == null) { + return null; + } + + const groupingsMap = new Map(); + + for (const f of data.folders) { + let folderView: FolderView; + if (data.encrypted) { + const folder = FolderWithIdExport.toDomain(f); + if (folder != null) { + folderView = await folder.decrypt(); + } + } else { + folderView = FolderWithIdExport.toView(f); + } + + if (folderView != null) { + groupingsMap.set(f.id, this.result.folders.length); + this.result.folders.push(folderView); + } + } + return groupingsMap; + } + + private async parseCollections( + data: BitwardenUnEncryptedOrgJsonExport | BitwardenEncryptedOrgJsonExport + ): Promise> | null { + if (data.collections == null) { + return null; + } + + const groupingsMap = new Map(); + + for (const c of data.collections) { + let collectionView: CollectionView; + if (data.encrypted) { + const collection = CollectionWithIdExport.toDomain(c); + collection.organizationId = this.organizationId; + collectionView = await collection.decrypt(); + } else { + collectionView = CollectionWithIdExport.toView(c); + collectionView.organizationId = null; + } + + if (collectionView != null) { + groupingsMap.set(c.id, this.result.collections.length); + this.result.collections.push(collectionView); + } + } + return groupingsMap; + } } diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts index 80872b68042..a8c2a711a0b 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts @@ -4,7 +4,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/exporter/vault-export/bitwarden-password-protected-types"; +import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/exporter/vault-export/bitwarden-json-export-types"; import { ImportResult } from "../../models/import-result"; import { Importer } from "../importer"; From 8593966a714c546c9e2c012e7bb748557df301d7 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Tue, 15 Aug 2023 15:15:13 -0400 Subject: [PATCH 017/135] PM-171 remove confirmation alerts for restoring an item (#5799) * remove confirmation alerts for restoring an item from trash and remove bulk-restore-dialog from vault individual and org --- apps/browser/src/_locales/en/messages.json | 3 - apps/desktop/src/locales/en/messages.json | 6 -- .../bulk-dialogs.module.ts | 15 +---- .../bulk-restore-dialog.component.html | 14 ---- .../bulk-restore-dialog.component.ts | 67 ------------------- .../vault/individual-vault/vault.component.ts | 25 +------ .../app/vault/org-vault/vault.component.ts | 25 +------ apps/web/src/locales/en/messages.json | 18 ----- .../vault/components/add-edit.component.ts | 10 --- .../src/vault/components/view.component.ts | 10 --- 10 files changed, 8 insertions(+), 185 deletions(-) delete mode 100644 apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.html delete mode 100644 apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 57ba39d673a..ab8446ee465 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-dialogs.module.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-dialogs.module.ts index 0618025bbfa..848f5629dae 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-dialogs.module.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-dialogs.module.ts @@ -4,22 +4,11 @@ import { SharedModule } from "../../../shared"; import { BulkDeleteDialogComponent } from "./bulk-delete-dialog/bulk-delete-dialog.component"; import { BulkMoveDialogComponent } from "./bulk-move-dialog/bulk-move-dialog.component"; -import { BulkRestoreDialogComponent } from "./bulk-restore-dialog/bulk-restore-dialog.component"; import { BulkShareDialogComponent } from "./bulk-share-dialog/bulk-share-dialog.component"; @NgModule({ imports: [SharedModule], - declarations: [ - BulkDeleteDialogComponent, - BulkMoveDialogComponent, - BulkRestoreDialogComponent, - BulkShareDialogComponent, - ], - exports: [ - BulkDeleteDialogComponent, - BulkMoveDialogComponent, - BulkRestoreDialogComponent, - BulkShareDialogComponent, - ], + declarations: [BulkDeleteDialogComponent, BulkMoveDialogComponent, BulkShareDialogComponent], + exports: [BulkDeleteDialogComponent, BulkMoveDialogComponent, BulkShareDialogComponent], }) export class BulkDialogsModule {} diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.html b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.html deleted file mode 100644 index 9cfe1bf69bc..00000000000 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.html +++ /dev/null @@ -1,14 +0,0 @@ - - - {{ "restoreSelected" | i18n }} - - - {{ "restoreSelectedItemsDesc" | i18n : cipherIds.length }} - - - - - - diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.ts deleted file mode 100644 index 94685f9fda0..00000000000 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; -import { Component, Inject } from "@angular/core"; - -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -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"; - -export interface BulkRestoreDialogParams { - cipherIds: string[]; - organization?: Organization; -} - -export enum BulkRestoreDialogResult { - Restored = "restored", - Canceled = "canceled", -} - -/** - * Strongly typed helper to open a BulkRestoreDialog - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param config Configuration for the dialog - */ -export const openBulkRestoreDialog = ( - dialogService: DialogServiceAbstraction, - config: DialogConfig -) => { - return dialogService.open( - BulkRestoreDialogComponent, - config - ); -}; - -@Component({ - templateUrl: "bulk-restore-dialog.component.html", -}) -export class BulkRestoreDialogComponent { - cipherIds: string[]; - organization?: Organization; - - constructor( - @Inject(DIALOG_DATA) params: BulkRestoreDialogParams, - private dialogRef: DialogRef, - private cipherService: CipherService, - private platformUtilsService: PlatformUtilsService, - private i18nService: I18nService - ) { - this.cipherIds = params.cipherIds ?? []; - this.organization = params.organization; - } - - submit = async () => { - const asAdmin = this.organization?.canEditAnyCollection; - await this.cipherService.restoreManyWithServer(this.cipherIds, this.organization?.id, asAdmin); - this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItems")); - this.close(BulkRestoreDialogResult.Restored); - }; - - protected cancel() { - this.close(BulkRestoreDialogResult.Canceled); - } - - private close(result: BulkRestoreDialogResult) { - this.dialogRef.close(result); - } -} diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index ba32a27aed8..b0cd2eb9581 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -75,10 +75,6 @@ import { BulkMoveDialogResult, openBulkMoveDialog, } from "./bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component"; -import { - BulkRestoreDialogResult, - openBulkRestoreDialog, -} from "./bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.component"; import { BulkShareDialogResult, openBulkShareDialog, @@ -683,16 +679,6 @@ export class VaultComponent implements OnInit, OnDestroy { return; } - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "restoreItemConfirmation" }, - content: { key: "restoreItem" }, - type: SimpleDialogType.WARNING, - }); - - if (!confirmed) { - return false; - } - try { await this.cipherService.restoreWithServer(c.id); this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem")); @@ -717,14 +703,9 @@ export class VaultComponent implements OnInit, OnDestroy { return; } - const dialog = openBulkRestoreDialog(this.dialogService, { - data: { cipherIds: selectedCipherIds }, - }); - - const result = await lastValueFrom(dialog.closed); - if (result === BulkRestoreDialogResult.Restored) { - this.refresh(); - } + await this.cipherService.restoreManyWithServer(selectedCipherIds); + this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItems")); + this.refresh(); } async deleteCipher(c: CipherView): Promise { 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 cfeafbf032c..dc12eeba49e 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -71,10 +71,6 @@ import { BulkDeleteDialogResult, openBulkDeleteDialog, } from "../individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component"; -import { - BulkRestoreDialogResult, - openBulkRestoreDialog, -} from "../individual-vault/bulk-action-dialogs/bulk-restore-dialog/bulk-restore-dialog.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"; @@ -669,16 +665,6 @@ export class VaultComponent implements OnInit, OnDestroy { return; } - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "restoreItem" }, - content: { key: "restoreItemConfirmation" }, - type: SimpleDialogType.WARNING, - }); - - if (!confirmed) { - return false; - } - try { const asAdmin = this.organization?.canEditAnyCollection; await this.cipherService.restoreWithServer(c.id, asAdmin); @@ -704,14 +690,9 @@ export class VaultComponent implements OnInit, OnDestroy { return; } - const dialog = openBulkRestoreDialog(this.dialogService, { - data: { cipherIds: selectedCipherIds, organization: this.organization }, - }); - - const result = await lastValueFrom(dialog.closed); - if (result === BulkRestoreDialogResult.Restored) { - this.refresh(); - } + await this.cipherService.restoreManyWithServer(selectedCipherIds); + this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItems")); + this.refresh(); } async deleteCipher(c: CipherView): Promise { diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 97ad4adb408..316dd8b68c8 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -3842,30 +3842,12 @@ "restoreSelected": { "message": "Restore selected" }, - "restoreItem": { - "message": "Restore item" - }, "restoredItem": { "message": "Item restored" }, "restoredItems": { "message": "Items restored" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, - "restoreItems": { - "message": "Restore items" - }, - "restoreSelectedItemsDesc": { - "message": "You have selected $COUNT$ item(s) to restore. Are you sure you want to restore these items?", - "placeholders": { - "count": { - "content": "$1", - "example": "150" - } - } - }, "restoredItemId": { "message": "Item $ID$ restored", "placeholders": { diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 52780ae3957..44da000a7ba 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -433,16 +433,6 @@ export class AddEditComponent implements OnInit, OnDestroy { return false; } - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "restoreItem" }, - content: { key: "restoreItemConfirmation" }, - type: SimpleDialogType.WARNING, - }); - - if (!confirmed) { - return false; - } - try { this.restorePromise = this.restoreCipher(); await this.restorePromise; diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index 1236e07bd79..7ba14b97f8f 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -209,16 +209,6 @@ export class ViewComponent implements OnDestroy, OnInit { return false; } - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "restoreItem" }, - content: { key: "restoreItemConfirmation" }, - type: SimpleDialogType.WARNING, - }); - - if (!confirmed) { - return false; - } - try { await this.restoreCipher(); this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem")); From 7b4faeaaca1b767eafde26e18a82a0fc23ecbdb1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Aug 2023 15:54:39 -0400 Subject: [PATCH 018/135] Update npm minor (#5724) * Update npm minor * Do not update past prettier major version --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Matt Gibson --- apps/cli/package.json | 10 +- apps/desktop/desktop_native/package.json | 2 +- .../package-lock.json | 8 +- .../native-messaging-test-runner/package.json | 2 +- .../vault-cipher-row.component.html | 6 +- .../src/navigation/nav-item.component.html | 4 +- package-lock.json | 6080 ++++++++++++----- package.json | 104 +- 8 files changed, 4255 insertions(+), 1961 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 7c6e50e22a2..0fc26e1fabf 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -49,29 +49,29 @@ "dependencies": { "@koa/multer": "3.0.2", "@koa/router": "12.0.0", - "argon2": "0.30.3", + "argon2": "0.31.0", "big-integer": "1.6.51", "browser-hrtime": "1.1.8", "chalk": "4.1.2", "commander": "7.2.0", "form-data": "4.0.0", "https-proxy-agent": "5.0.1", - "inquirer": "8.2.5", + "inquirer": "8.2.6", "jsdom": "22.1.0", "jszip": "3.10.1", "koa": "2.14.2", - "koa-bodyparser": "4.4.0", + "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", "lowdb": "1.0.0", "lunr": "2.3.9", "multer": "1.4.5-lts.1", - "node-fetch": "2.6.11", + "node-fetch": "2.6.12", "node-forge": "1.3.1", "open": "8.4.2", "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.0.5", + "tldts": "6.0.14", "zxcvbn": "4.4.2" } } diff --git a/apps/desktop/desktop_native/package.json b/apps/desktop/desktop_native/package.json index fe37602768e..44958043707 100644 --- a/apps/desktop/desktop_native/package.json +++ b/apps/desktop/desktop_native/package.json @@ -11,7 +11,7 @@ "author": "", "license": "GPL-3.0", "devDependencies": { - "@napi-rs/cli": "2.16.1" + "@napi-rs/cli": "2.16.2" }, "napi": { "name": "desktop_native", diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index d90b46ea702..324cbee1bea 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@tsconfig/node16": "1.0.4", - "@types/node": "18.16.16", + "@types/node": "18.17.5", "@types/node-ipc": "9.2.0", "typescript": "4.7.4" } @@ -99,9 +99,9 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" }, "node_modules/@types/node": { - "version": "18.16.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.16.tgz", - "integrity": "sha512-NpaM49IGQQAUlBhHMF82QH80J08os4ZmyF9MkpCzWAGuOHqE4gTEbhzd7L3l5LmWuZ6E0OiC1FweQ4tsiW35+g==" + "version": "18.17.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.5.tgz", + "integrity": "sha512-xNbS75FxH6P4UXTPUJp/zNPq6/xsfdJKussCWNOnz4aULWIRwMgP1LgaB5RiBnMX1DPCYenuqGZfnIAx5mbFLA==" }, "node_modules/@types/node-ipc": { "version": "9.2.0", diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 70e69fd51ae..002138d3b57 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@tsconfig/node16": "1.0.4", - "@types/node": "18.16.16", + "@types/node": "18.17.5", "@types/node-ipc": "9.2.0", "typescript": "4.7.4" }, 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 664b3bbc380..07d061311cf 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 @@ -11,11 +11,7 @@ - +
diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts similarity index 72% rename from libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts rename to libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts index d8824cd7dd4..f9acdbe8c6e 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts @@ -2,37 +2,30 @@ import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; import { FormGroup } from "@angular/forms"; -import { - SimpleDialogType, - SimpleDialogCloseType, - Translation, -} from "@bitwarden/angular/services/dialog"; -import { SimpleDialogOptions } from "@bitwarden/angular/services/dialog/simple-dialog-options"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SimpleDialogOptions, SimpleDialogType, Translation } from "../.."; + const DEFAULT_ICON: Record = { - [SimpleDialogType.PRIMARY]: "bwi-business", - [SimpleDialogType.SUCCESS]: "bwi-star", - [SimpleDialogType.INFO]: "bwi-info-circle", - [SimpleDialogType.WARNING]: "bwi-exclamation-triangle", - [SimpleDialogType.DANGER]: "bwi-error", + primary: "bwi-business", + success: "bwi-star", + info: "bwi-info-circle", + warning: "bwi-exclamation-triangle", + danger: "bwi-error", }; const DEFAULT_COLOR: Record = { - [SimpleDialogType.PRIMARY]: "tw-text-primary-500", - [SimpleDialogType.SUCCESS]: "tw-text-success", - [SimpleDialogType.INFO]: "tw-text-info", - [SimpleDialogType.WARNING]: "tw-text-warning", - [SimpleDialogType.DANGER]: "tw-text-danger", + primary: "tw-text-primary-500", + success: "tw-text-success", + info: "tw-text-info", + warning: "tw-text-warning", + danger: "tw-text-danger", }; @Component({ templateUrl: "./simple-configurable-dialog.component.html", }) export class SimpleConfigurableDialogComponent { - protected SimpleDialogType = SimpleDialogType; - protected SimpleDialogCloseType = SimpleDialogCloseType; - get iconClasses() { return [ this.simpleDialogOpts.icon ?? DEFAULT_ICON[this.simpleDialogOpts.type], @@ -61,7 +54,7 @@ export class SimpleConfigurableDialogComponent { await this.simpleDialogOpts.acceptAction(); } - this.dialogRef.close(SimpleDialogCloseType.ACCEPT); + this.dialogRef.close(true); }; private localizeText() { diff --git a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts similarity index 84% rename from libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts rename to libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts index 0982db1ca7a..81400537be5 100644 --- a/libs/components/src/dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.service.stories.ts @@ -1,17 +1,13 @@ import { Component } from "@angular/core"; import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; -import { - SimpleDialogType, - SimpleDialogOptions, - DialogServiceAbstraction, -} from "@bitwarden/angular/services/dialog"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { ButtonModule } from "../../button"; -import { CalloutModule } from "../../callout"; -import { I18nMockService } from "../../utils/i18n-mock.service"; -import { DialogModule } from "../dialog.module"; +import { SimpleDialogOptions, DialogService } from "../.."; +import { ButtonModule } from "../../../button"; +import { CalloutModule } from "../../../callout"; +import { I18nMockService } from "../../../utils/i18n-mock.service"; +import { DialogModule } from "../../dialog.module"; @Component({ template: ` @@ -41,27 +37,27 @@ class StoryDialogComponent { { title: this.i18nService.t("primaryTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, + type: "primary", }, { title: this.i18nService.t("successTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.SUCCESS, + type: "success", }, { title: this.i18nService.t("infoTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.INFO, + type: "info", }, { title: this.i18nService.t("warningTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.WARNING, + type: "warning", }, { title: this.i18nService.t("dangerTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.DANGER, + type: "danger", }, ], }, @@ -71,21 +67,21 @@ class StoryDialogComponent { { title: this.i18nService.t("primaryTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, + type: "primary", acceptButtonText: "Ok", cancelButtonText: null, }, { title: this.i18nService.t("primaryTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, + type: "primary", acceptButtonText: this.i18nService.t("accept"), cancelButtonText: this.i18nService.t("decline"), }, { title: this.i18nService.t("primaryTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, + type: "primary", acceptButtonText: "Ok", }, ], @@ -96,7 +92,7 @@ class StoryDialogComponent { { title: this.i18nService.t("primaryTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, + type: "primary", icon: "bwi-family", }, ], @@ -107,7 +103,7 @@ class StoryDialogComponent { { title: this.i18nService.t("primaryTypeSimpleDialog"), content: this.i18nService.t("dialogContent"), - type: SimpleDialogType.PRIMARY, + type: "primary", disableClose: true, }, { @@ -116,7 +112,7 @@ class StoryDialogComponent { acceptAction: () => { return new Promise((resolve) => setTimeout(resolve, 10000)); }, - type: SimpleDialogType.PRIMARY, + type: "primary", }, ], }, @@ -126,7 +122,7 @@ class StoryDialogComponent { calloutType = "info"; dialogCloseResult: boolean; - constructor(public dialogService: DialogServiceAbstraction, private i18nService: I18nService) {} + constructor(public dialogService: DialogService, private i18nService: I18nService) {} async openSimpleConfigurableDialog(opts: SimpleDialogOptions) { this.dialogCloseResult = await this.dialogService.openSimpleDialog(opts); diff --git a/libs/components/src/dialog/simple-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts similarity index 80% rename from libs/components/src/dialog/simple-dialog.service.stories.ts rename to libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts index 5050af35b6f..b6615184ecb 100644 --- a/libs/components/src/dialog/simple-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts @@ -4,15 +4,15 @@ import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { ButtonModule } from "../button"; -import { IconButtonModule } from "../icon-button"; -import { SharedModule } from "../shared/shared.module"; -import { I18nMockService } from "../utils/i18n-mock.service"; +import { ButtonModule } from "../../button"; +import { IconButtonModule } from "../../icon-button"; +import { SharedModule } from "../../shared/shared.module"; +import { I18nMockService } from "../../utils/i18n-mock.service"; +import { DialogService } from "../dialog.service"; +import { DialogCloseDirective } from "../directives/dialog-close.directive"; +import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive"; -import { DialogService } from "./dialog.service"; -import { DialogCloseDirective } from "./directives/dialog-close.directive"; -import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive"; -import { SimpleDialogComponent } from "./simple-dialog/simple-dialog.component"; +import { SimpleDialogComponent } from "./simple-dialog.component"; interface Animal { animal: string; diff --git a/libs/angular/src/services/dialog/simple-dialog-options.ts b/libs/components/src/dialog/simple-dialog/types.ts similarity index 89% rename from libs/angular/src/services/dialog/simple-dialog-options.ts rename to libs/components/src/dialog/simple-dialog/types.ts index aec6f00e32f..4032b499cbe 100644 --- a/libs/angular/src/services/dialog/simple-dialog-options.ts +++ b/libs/components/src/dialog/simple-dialog/types.ts @@ -1,5 +1,7 @@ -import { SimpleDialogType } from "./simple-dialog-type.enum"; -import { Translation } from "./translation"; +export interface Translation { + key: string; + placeholders?: Array; +} // Using type lets devs skip optional params w/out having to pass undefined. /** @@ -55,3 +57,5 @@ export type SimpleDialogOptions = { */ acceptAction?: () => Promise; }; + +export type SimpleDialogType = "primary" | "success" | "info" | "warning" | "danger"; diff --git a/libs/components/src/search/search.stories.ts b/libs/components/src/search/search.stories.ts index c68d6112809..d4af6e676ae 100644 --- a/libs/components/src/search/search.stories.ts +++ b/libs/components/src/search/search.stories.ts @@ -1,10 +1,10 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { InputModule } from "../input/input.module"; +import { SharedModule } from "../shared"; import { I18nMockService } from "../utils/i18n-mock.service"; import { SearchComponent } from "./search.component"; @@ -14,7 +14,7 @@ export default { component: SearchComponent, decorators: [ moduleMetadata({ - imports: [InputModule, FormsModule, ReactiveFormsModule, JslibModule], + imports: [SharedModule, InputModule, FormsModule, ReactiveFormsModule], providers: [ { provide: I18nService, diff --git a/libs/shared/tsconfig.libs.json b/libs/shared/tsconfig.libs.json index 28a1ccdfa4c..18b7b1712a4 100644 --- a/libs/shared/tsconfig.libs.json +++ b/libs/shared/tsconfig.libs.json @@ -5,6 +5,7 @@ "@bitwarden/angular/*": ["../angular/src/*"], "@bitwarden/auth": ["../auth/src/*"], "@bitwarden/common/*": ["../common/src/*"], + "@bitwarden/components": ["../components/src"], "@bitwarden/exporter/*": ["../exporter/src/*"], "@bitwarden/importer": ["../importer/src"], "@bitwarden/node/*": ["../node/src/*"] From e016ed001e1f6f40390e7fccc2ad4e8e0249228f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 16 Aug 2023 16:17:03 +0200 Subject: [PATCH 025/135] [PM-2899] Implement ProtonPass json importer (#5766) * Implement ProtonPass json importer * Add protonpass-importer json type definition * Fix alphabetical order in importer imports * Add importer error message for encrypted protonpass imports * Add i18n to protonpass importer * Add protonpass (zip) importer * Fix protonpass importer * Add unit tests for protonpass importer * Make protonpass importer not discard totp codes * Merge protonpass json & zip importers * Add protonpass creditcard import & fix note import * Fix protonpass zip import not recognizing zip files on windows/chrome * Make protonpass importer use vault types * Make protonpass importer treat vaults as folders * Make protonpass importer treat folders as collections for organizations Co-authored-by: Daniel James Smith * Add types to protonpass test data * Fix protonpass importer's moveFoldersToCollections * Add tests for folders/collections * Remove unecessary type cast in protonpass importer * Remove unecessary type annotations in protonpass importer * Add assertion for credit card cvv in protonpass importer * Handle trashed items in protonpass importer * Fix setting expiry month on credit cards * Fix wrong folder-assignment Only the first item of a "vault" was getting assigned to a folder Extend unit tests to verify behaviour --------- Co-authored-by: Daniel James Smith Co-authored-by: Daniel James Smith --- apps/cli/src/locales/en/messages.json | 3 + apps/cli/src/tools/import.command.ts | 4 +- apps/cli/src/utils.ts | 5 +- .../tools/import-export/import.component.html | 4 + .../tools/import-export/import.component.ts | 16 +- .../spec/protonpass-json-importer.spec.ts | 117 ++++++++++++ .../protonpass-json/protonpass.json.ts | 174 ++++++++++++++++++ libs/importer/src/importers/index.ts | 1 + .../protonpass/protonpass-json-importer.ts | 105 +++++++++++ .../protonpass/types/protonpass-json-type.ts | 72 ++++++++ libs/importer/src/models/import-options.ts | 1 + libs/importer/src/services/import.service.ts | 3 + 12 files changed, 498 insertions(+), 7 deletions(-) create mode 100644 libs/importer/spec/protonpass-json-importer.spec.ts create mode 100644 libs/importer/spec/test-data/protonpass-json/protonpass.json.ts create mode 100644 libs/importer/src/importers/protonpass/protonpass-json-importer.ts create mode 100644 libs/importer/src/importers/protonpass/types/protonpass-json-type.ts diff --git a/apps/cli/src/locales/en/messages.json b/apps/cli/src/locales/en/messages.json index 4b6524b7ccb..e12b30af2c0 100644 --- a/apps/cli/src/locales/en/messages.json +++ b/apps/cli/src/locales/en/messages.json @@ -46,5 +46,8 @@ }, "ssoKeyConnectorError": { "message": "Key Connector error: make sure Key Connector is available and working correctly." + }, + "unsupportedEncryptedImport": { + "message": "Importing encrypted files is currently not supported." } } diff --git a/apps/cli/src/tools/import.command.ts b/apps/cli/src/tools/import.command.ts index 0080197aa74..c013d3c6b62 100644 --- a/apps/cli/src/tools/import.command.ts +++ b/apps/cli/src/tools/import.command.ts @@ -67,7 +67,9 @@ export class ImportCommand { try { let contents; if (format === "1password1pux") { - contents = await CliUtils.extract1PuxContent(filepath); + contents = await CliUtils.extractZipContent(filepath, "export.data"); + } else if (format === "protonpass" && filepath.endsWith(".zip")) { + contents = await CliUtils.extractZipContent(filepath, "Proton Pass/data.json"); } else { contents = await CliUtils.readFile(filepath); } diff --git a/apps/cli/src/utils.ts b/apps/cli/src/utils.ts index d5e442ded19..f8780dbec63 100644 --- a/apps/cli/src/utils.ts +++ b/apps/cli/src/utils.ts @@ -46,7 +46,7 @@ export class CliUtils { }); } - static extract1PuxContent(input: string): Promise { + static extractZipContent(input: string, filepath: string): Promise { return new Promise((resolve, reject) => { let p: string = null; if (input != null && input !== "") { @@ -65,7 +65,7 @@ export class CliUtils { } JSZip.loadAsync(data).then( (zip) => { - resolve(zip.file("export.data").async("string")); + resolve(zip.file(filepath).async("string")); }, (reason) => { reject(reason); @@ -74,6 +74,7 @@ export class CliUtils { }); }); } + /** * Save the given data to a file and determine the target file if necessary. * If output is non-empty, it is used as target filename. Otherwise the target filename is diff --git a/apps/web/src/app/tools/import-export/import.component.html b/apps/web/src/app/tools/import-export/import.component.html index 67c2fe45b2a..c9e3285c291 100644 --- a/apps/web/src/app/tools/import-export/import.component.html +++ b/apps/web/src/app/tools/import-export/import.component.html @@ -341,6 +341,10 @@ Log in to "https://vault.passky.org" → "Import & Export" → "Export" in the Passky section. ("Backup" is unsupported as it is encrypted). + + In the ProtonPass browser extension, go to Settings > Export. Export without PGP encryption + and save the zip file. + {{ "selectImportFile" | i18n }} diff --git a/apps/web/src/app/tools/import-export/import.component.ts b/apps/web/src/app/tools/import-export/import.component.ts index 58f30b4ee48..eb9201f021d 100644 --- a/apps/web/src/app/tools/import-export/import.component.ts +++ b/apps/web/src/app/tools/import-export/import.component.ts @@ -326,7 +326,15 @@ export class ImportComponent implements OnInit, OnDestroy { private getFileContents(file: File): Promise { if (this.format === "1password1pux") { - return this.extract1PuxContent(file); + return this.extractZipContent(file, "export.data"); + } + if ( + this.format === "protonpass" && + (file.type === "application/zip" || + file.type == "application/x-zip-compressed" || + file.name.endsWith(".zip")) + ) { + return this.extractZipContent(file, "Proton Pass/data.json"); } return new Promise((resolve, reject) => { @@ -353,11 +361,11 @@ export class ImportComponent implements OnInit, OnDestroy { }); } - private extract1PuxContent(file: File): Promise { + private extractZipContent(zipFile: File, contentFilePath: string): Promise { return new JSZip() - .loadAsync(file) + .loadAsync(zipFile) .then((zip) => { - return zip.file("export.data").async("string"); + return zip.file(contentFilePath).async("string"); }) .then( function success(content) { diff --git a/libs/importer/spec/protonpass-json-importer.spec.ts b/libs/importer/spec/protonpass-json-importer.spec.ts new file mode 100644 index 00000000000..b8d57b9be7b --- /dev/null +++ b/libs/importer/spec/protonpass-json-importer.spec.ts @@ -0,0 +1,117 @@ +import { MockProxy } from "jest-mock-extended"; + +import { FieldType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; + +import { ProtonPassJsonImporter } from "../src/importers"; + +import { testData } from "./test-data/protonpass-json/protonpass.json"; + +describe("Protonpass Json Importer", () => { + let importer: ProtonPassJsonImporter; + let i18nService: MockProxy; + beforeEach(() => { + importer = new ProtonPassJsonImporter(i18nService); + }); + + it("should parse login data", async () => { + const testDataJson = JSON.stringify(testData); + + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.name).toEqual("Test Login - Personal Vault"); + expect(cipher.type).toEqual(CipherType.Login); + expect(cipher.login.username).toEqual("Username"); + expect(cipher.login.password).toEqual("Password"); + expect(cipher.login.uris.length).toEqual(2); + const uriView = cipher.login.uris.shift(); + expect(uriView.uri).toEqual("https://example.com/"); + expect(cipher.notes).toEqual("My login secure note."); + + expect(cipher.fields.at(2).name).toEqual("second 2fa secret"); + expect(cipher.fields.at(2).value).toEqual("TOTPCODE"); + expect(cipher.fields.at(2).type).toEqual(FieldType.Hidden); + }); + + it("should parse note data", async () => { + const testDataJson = JSON.stringify(testData); + + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + result.ciphers.shift(); + const noteCipher = result.ciphers.shift(); + expect(noteCipher.type).toEqual(CipherType.SecureNote); + expect(noteCipher.name).toEqual("My Secure Note"); + expect(noteCipher.notes).toEqual("Secure note contents."); + }); + + it("should parse credit card data", async () => { + const testDataJson = JSON.stringify(testData); + + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + result.ciphers.shift(); + result.ciphers.shift(); + + const creditCardCipher = result.ciphers.shift(); + expect(creditCardCipher.type).toBe(CipherType.Card); + expect(creditCardCipher.card.number).toBe("1234222233334444"); + expect(creditCardCipher.card.cardholderName).toBe("Test name"); + expect(creditCardCipher.card.expMonth).toBe("1"); + expect(creditCardCipher.card.expYear).toBe("2025"); + expect(creditCardCipher.card.code).toBe("333"); + expect(creditCardCipher.fields.at(0).name).toEqual("PIN"); + expect(creditCardCipher.fields.at(0).value).toEqual("1234"); + expect(creditCardCipher.fields.at(0).type).toEqual(FieldType.Hidden); + }); + + it("should create folders if not part of an organization", async () => { + const testDataJson = JSON.stringify(testData); + const result = await importer.parse(testDataJson); + + const folders = result.folders; + expect(folders.length).toBe(2); + expect(folders[0].name).toBe("Personal"); + expect(folders[1].name).toBe("Test"); + + // "My Secure Note" is assigned to folder "Personal" + expect(result.folderRelationships[1]).toEqual([1, 0]); + // "Other vault login" is assigned to folder "Test" + expect(result.folderRelationships[3]).toEqual([3, 1]); + }); + + it("should create collections if part of an organization", async () => { + const testDataJson = JSON.stringify(testData); + importer.organizationId = Utils.newGuid(); + const result = await importer.parse(testDataJson); + expect(result != null).toBe(true); + + const collections = result.collections; + expect(collections.length).toBe(2); + expect(collections[0].name).toBe("Personal"); + expect(collections[1].name).toBe("Test"); + + // "My Secure Note" is assigned to folder "Personal" + expect(result.collectionRelationships[1]).toEqual([1, 0]); + // "Other vault login" is assigned to folder "Test" + expect(result.collectionRelationships[3]).toEqual([3, 1]); + }); + + it("should not add deleted items", async () => { + const testDataJson = JSON.stringify(testData); + const result = await importer.parse(testDataJson); + + const ciphers = result.ciphers; + for (const cipher of ciphers) { + expect(cipher.name).not.toBe("My Deleted Note"); + } + + expect(ciphers.length).toBe(4); + }); +}); diff --git a/libs/importer/spec/test-data/protonpass-json/protonpass.json.ts b/libs/importer/spec/test-data/protonpass-json/protonpass.json.ts new file mode 100644 index 00000000000..4217ddd4d5b --- /dev/null +++ b/libs/importer/spec/test-data/protonpass-json/protonpass.json.ts @@ -0,0 +1,174 @@ +import { ProtonPassJsonFile } from "../../../src/importers/protonpass/types/protonpass-json-type"; + +export const testData: ProtonPassJsonFile = { + version: "1.3.1", + userId: "REDACTED_USER_ID", + encrypted: false, + vaults: { + REDACTED_VAULT_ID_A: { + name: "Personal", + description: "Personal vault", + display: { + color: 0, + icon: 0, + }, + items: [ + { + itemId: + "yZENmDjtmZGODNy3Q_CZiPAF_IgINq8w-R-qazrOh-Nt9YJeVF3gu07ovzDS4jhYHoMdOebTw5JkYPGgIL1mwQ==", + shareId: + "SN5uWo4WZF2uT5wIDqtbdpkjuxCbNTOIdf-JQ_DYZcKYKURHiZB5csS1a1p9lklvju9ni42l08IKzwQG0B2ySg==", + data: { + metadata: { + name: "Test Login - Personal Vault", + note: "My login secure note.", + itemUuid: "e8ee1a0c", + }, + extraFields: [ + { + fieldName: "non-hidden field", + type: "text", + data: { + content: "non-hidden field content", + }, + }, + { + fieldName: "hidden field", + type: "hidden", + data: { + content: "hidden field content", + }, + }, + { + fieldName: "second 2fa secret", + type: "totp", + data: { + totpUri: "TOTPCODE", + }, + }, + ], + type: "login", + content: { + username: "Username", + password: "Password", + urls: ["https://example.com/", "https://example2.com/"], + totpUri: + "otpauth://totp/Test%20Login%20-%20Personal%20Vault:Username?issuer=Test%20Login%20-%20Personal%20Vault&secret=TOTPCODE&algorithm=SHA1&digits=6&period=30", + }, + }, + state: 1, + aliasEmail: null, + contentFormatVersion: 1, + createTime: 1689182868, + modifyTime: 1689182868, + }, + { + itemId: + "xqq_Bh8RxNMBerkiMvRdH427yswZznjYwps-f6C5D8tmKiPgMxCSPNz1BOd4nRJ309gciDiPhXcCVWOyfJ66ZA==", + shareId: + "SN5uWo4WZF2uT5wIDqtbdpkjuxCbNTOIdf-JQ_DYZcKYKURHiZB5csS1a1p9lklvju9ni42l08IKzwQG0B2ySg==", + data: { + metadata: { + name: "My Secure Note", + note: "Secure note contents.", + itemUuid: "ad618070", + }, + extraFields: [], + type: "note", + content: {}, + }, + state: 1, + aliasEmail: null, + contentFormatVersion: 1, + createTime: 1689182908, + modifyTime: 1689182908, + }, + { + itemId: + "ZmGzd-HNQYTr6wmfWlSfiStXQLqGic_PYB2Q2T_hmuRM2JIA4pKAPJcmFafxJrDpXxLZ2EPjgD6Noc9a0U6AVQ==", + shareId: + "SN5uWo4WZF2uT5wIDqtbdpkjuxCbNTOIdf-JQ_DYZcKYKURHiZB5csS1a1p9lklvju9ni42l08IKzwQG0B2ySg==", + data: { + metadata: { + name: "Test Card", + note: "Credit Card Note", + itemUuid: "d8f45370", + }, + extraFields: [], + type: "creditCard", + content: { + cardholderName: "Test name", + cardType: 0, + number: "1234222233334444", + verificationNumber: "333", + expirationDate: "012025", + pin: "1234", + }, + }, + state: 1, + aliasEmail: null, + contentFormatVersion: 1, + createTime: 1691001643, + modifyTime: 1691001643, + }, + { + itemId: + "xqq_Bh8RxNMBerkiMvRdH427yswZznjYwps-f6C5D8tmKiPgMxCSPNz1BOd4nRJ309gciDiPhXcCVWOyfJ66ZA==", + shareId: + "SN5uWo4WZF2uT5wIDqtbdpkjuxCbNTOIdf-JQ_DYZcKYKURHiZB5csS1a1p9lklvju9ni42l08IKzwQG0B2ySg==", + data: { + metadata: { + name: "My Deleted Note", + note: "Secure note contents.", + itemUuid: "ad618070", + }, + extraFields: [], + type: "note", + content: {}, + }, + state: 2, + aliasEmail: null, + contentFormatVersion: 1, + createTime: 1689182908, + modifyTime: 1689182908, + }, + ], + }, + REDACTED_VAULT_ID_B: { + name: "Test", + description: "", + display: { + color: 4, + icon: 2, + }, + items: [ + { + itemId: + "U_J8-eUR15sC-PjUhjVcixDcayhjGuoerUZCr560RlAi0ZjBNkSaSKAytVzZn4E0hiFX1_y4qZbUetl6jO3aJw==", + shareId: + "OJz-4MnPqAuYnyemhctcGDlSLJrzsTnf2FnFSwxh1QP_oth9xyGDc2ZAqCv5FnqkVgTNHT5aPj62zcekNemfNw==", + data: { + metadata: { + name: "Other vault login", + note: "", + itemUuid: "f3429d44", + }, + extraFields: [], + type: "login", + content: { + username: "other vault username", + password: "other vault password", + urls: [], + totpUri: "", + }, + }, + state: 1, + aliasEmail: null, + contentFormatVersion: 1, + createTime: 1689182949, + modifyTime: 1689182949, + }, + ], + }, + }, +}; diff --git a/libs/importer/src/importers/index.ts b/libs/importer/src/importers/index.ts index 1a479a4cf5f..fff2edf3d54 100644 --- a/libs/importer/src/importers/index.ts +++ b/libs/importer/src/importers/index.ts @@ -44,6 +44,7 @@ export { PasswordBossJsonImporter } from "./passwordboss-json-importer"; export { PasswordDragonXmlImporter } from "./passworddragon-xml-importer"; export { PasswordSafeXmlImporter } from "./passwordsafe-xml-importer"; export { PasswordWalletTxtImporter } from "./passwordwallet-txt-importer"; +export { ProtonPassJsonImporter } from "./protonpass/protonpass-json-importer"; export { PsonoJsonImporter } from "./psono/psono-json-importer"; export { RememBearCsvImporter } from "./remembear-csv-importer"; export { RoboFormCsvImporter } from "./roboform-csv-importer"; diff --git a/libs/importer/src/importers/protonpass/protonpass-json-importer.ts b/libs/importer/src/importers/protonpass/protonpass-json-importer.ts new file mode 100644 index 00000000000..86f4444ec4d --- /dev/null +++ b/libs/importer/src/importers/protonpass/protonpass-json-importer.ts @@ -0,0 +1,105 @@ +import { FieldType, SecureNoteType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; +import { CardView } from "@bitwarden/common/vault/models/view/card.view"; +import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view"; + +import { ImportResult } from "../../models/import-result"; +import { BaseImporter } from "../base-importer"; +import { Importer } from "../importer"; + +import { + ProtonPassCreditCardItemContent, + ProtonPassItemState, + ProtonPassJsonFile, + ProtonPassLoginItemContent, +} from "./types/protonpass-json-type"; + +export class ProtonPassJsonImporter extends BaseImporter implements Importer { + constructor(private i18nService: I18nService) { + super(); + } + + parse(data: string): Promise { + const result = new ImportResult(); + const results: ProtonPassJsonFile = JSON.parse(data); + if (results == null || results.vaults == null) { + result.success = false; + return Promise.resolve(result); + } + + if (results.encrypted) { + result.success = false; + result.errorMessage = this.i18nService.t("unsupportedEncryptedImport"); + return Promise.resolve(result); + } + + for (const [, vault] of Object.entries(results.vaults)) { + for (const item of vault.items) { + if (item.state == ProtonPassItemState.TRASHED) { + continue; + } + this.processFolder(result, vault.name); + + const cipher = this.initLoginCipher(); + cipher.name = item.data.metadata.name; + cipher.notes = item.data.metadata.note; + + switch (item.data.type) { + case "login": { + const loginContent = item.data.content as ProtonPassLoginItemContent; + cipher.login.uris = this.makeUriArray(loginContent.urls); + cipher.login.username = loginContent.username; + cipher.login.password = loginContent.password; + if (loginContent.totpUri != "") { + cipher.login.totp = new URL(loginContent.totpUri).searchParams.get("secret"); + } + for (const extraField of item.data.extraFields) { + this.processKvp( + cipher, + extraField.fieldName, + extraField.type == "totp" ? extraField.data.totpUri : extraField.data.content, + extraField.type == "text" ? FieldType.Text : FieldType.Hidden + ); + } + break; + } + case "note": + cipher.type = CipherType.SecureNote; + cipher.secureNote = new SecureNoteView(); + cipher.secureNote.type = SecureNoteType.Generic; + break; + case "creditCard": { + const creditCardContent = item.data.content as ProtonPassCreditCardItemContent; + cipher.type = CipherType.Card; + cipher.card = new CardView(); + cipher.card.cardholderName = creditCardContent.cardholderName; + cipher.card.number = creditCardContent.number; + cipher.card.brand = CardView.getCardBrandByPatterns(creditCardContent.number); + cipher.card.code = creditCardContent.verificationNumber; + + if (!this.isNullOrWhitespace(creditCardContent.expirationDate)) { + cipher.card.expMonth = creditCardContent.expirationDate.substring(0, 2); + cipher.card.expMonth = cipher.card.expMonth.replace(/^0+/, ""); + cipher.card.expYear = creditCardContent.expirationDate.substring(2, 6); + } + + if (!this.isNullOrWhitespace(creditCardContent.pin)) { + this.processKvp(cipher, "PIN", creditCardContent.pin, FieldType.Hidden); + } + + break; + } + } + + this.cleanupCipher(cipher); + result.ciphers.push(cipher); + } + } + if (this.organization) { + this.moveFoldersToCollections(result); + } + result.success = true; + return Promise.resolve(result); + } +} diff --git a/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts b/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts new file mode 100644 index 00000000000..27b38f434e7 --- /dev/null +++ b/libs/importer/src/importers/protonpass/types/protonpass-json-type.ts @@ -0,0 +1,72 @@ +export type ProtonPassJsonFile = { + version: string; + userId: string; + encrypted: boolean; + vaults: Record; +}; + +export type ProtonPassVault = { + name: string; + description: string; + display: { + color: number; + icon: number; + }; + items: ProtonPassItem[]; +}; + +export type ProtonPassItem = { + itemId: string; + shareId: string; + data: ProtonPassItemData; + state: ProtonPassItemState; + aliasEmail: string | null; + contentFormatVersion: number; + createTime: number; + modifyTime: number; +}; + +export enum ProtonPassItemState { + ACTIVE = 1, + TRASHED = 2, +} + +export type ProtonPassItemData = { + metadata: ProtonPassItemMetadata; + extraFields: ProtonPassItemExtraField[]; + type: "login" | "alias" | "creditCard" | "note"; + content: ProtonPassLoginItemContent | ProtonPassCreditCardItemContent; +}; + +export type ProtonPassItemMetadata = { + name: string; + note: string; + itemUuid: string; +}; + +export type ProtonPassItemExtraField = { + fieldName: string; + type: string; + data: ProtonPassItemExtraFieldData; +}; + +export type ProtonPassItemExtraFieldData = { + content?: string; + totpUri?: string; +}; + +export type ProtonPassLoginItemContent = { + username?: string; + password?: string; + urls?: string[]; + totpUri?: string; +}; + +export type ProtonPassCreditCardItemContent = { + cardholderName?: string; + cardType?: number; + number?: string; + verificationNumber?: string; + expirationDate?: string; + pin?: string; +}; diff --git a/libs/importer/src/models/import-options.ts b/libs/importer/src/models/import-options.ts index 8a8ead96a49..2afe801d202 100644 --- a/libs/importer/src/models/import-options.ts +++ b/libs/importer/src/models/import-options.ts @@ -27,6 +27,7 @@ export const regularImportOptions = [ // { id: "keeperjson", name: "Keeper (json)" }, { id: "enpasscsv", name: "Enpass (csv)" }, { id: "enpassjson", name: "Enpass (json)" }, + { id: "protonpass", name: "ProtonPass (zip/json)" }, { id: "safeincloudxml", name: "SafeInCloud (xml)" }, { id: "pwsafexml", name: "Password Safe (xml)" }, { id: "stickypasswordxml", name: "Sticky Password (xml)" }, diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 5920ec200d4..4ff15174c56 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -62,6 +62,7 @@ import { PasswordDragonXmlImporter, PasswordSafeXmlImporter, PasswordWalletTxtImporter, + ProtonPassJsonImporter, PsonoJsonImporter, RememBearCsvImporter, RoboFormCsvImporter, @@ -319,6 +320,8 @@ export class ImportService implements ImportServiceAbstraction { return new PsonoJsonImporter(); case "passkyjson": return new PasskyJsonImporter(); + case "protonpass": + return new ProtonPassJsonImporter(this.i18nService); default: return null; } From d2bff7eb8b9dc23527668b70c927fd13c8ef1090 Mon Sep 17 00:00:00 2001 From: Opeyemi Date: Wed, 16 Aug 2023 16:41:02 +0100 Subject: [PATCH 026/135] [DEVOPS-1528] - Update clients to point build and release to Prod ACR (#6026) * update clients to point build and release to Prod ACR * FIX: typo * FIX: run block * UPDATE: add suggestions * UPDATE: workflow runners and job needs * UPDATE: registry env in build * UPDATE: suggestion --- .github/workflows/build-web.yml | 79 ++++++++++------------------- .github/workflows/release-web.yml | 83 +++++++++++-------------------- 2 files changed, 54 insertions(+), 108 deletions(-) diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 9c670f01c65..b17cb816336 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -31,6 +31,9 @@ on: description: "Custom image tag extension" required: false +env: + _AZ_REGISTRY: bitwardenprod.azurecr.io + jobs: cloc: name: CLOC @@ -65,8 +68,7 @@ jobs: build-artifacts: name: Build artifacts runs-on: ubuntu-22.04 - needs: - - setup + needs: setup env: _VERSION: ${{ needs.setup.outputs.version }} strategy: @@ -146,13 +148,10 @@ jobs: matrix: include: - artifact_name: cloud-QA - registries: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] image_name: web-qa-cloud - artifact_name: ee - registries: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] image_name: web-ee - artifact_name: selfhosted-COMMERCIAL - registries: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] image_name: web env: _VERSION: ${{ needs.setup.outputs.version }} @@ -174,15 +173,7 @@ jobs: fi ########## ACRs ########## - - name: Login to Azure - QA - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 - with: - creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }} - - - name: Log into QA container registry - run: az acr login -n bitwardenqa - - - name: Login to Azure - Prod + - name: Login to Prod Azure uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} @@ -190,6 +181,18 @@ jobs: - name: Log into Prod container registry run: az acr login -n bitwardenprod + - name: Login to Azure - CI Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve github PAT secrets + id: retrieve-secret-pat + uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + with: + keyvault: "bitwarden-ci" + secrets: "github-pat-bitwarden-devops-bot-repo-scope" + - name: Download ${{ matrix.artifact_name }} artifact uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: @@ -218,37 +221,17 @@ jobs: echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT - - name: Generate tag list - id: tag-list - env: - IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} - PROJECT_NAME: ${{ matrix.image_name }} - run: echo "tags=bitwardenqa.azurecr.io/${PROJECT_NAME}:${IMAGE_TAG},bitwardenprod.azurecr.io/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT - ########## Build Image ########## - name: Extract artifact working-directory: apps/web run: unzip web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip - - name: Login to Azure - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 - with: - creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - - - name: Retrieve github PAT secrets - id: retrieve-secret-pat - uses: bitwarden/gh-actions/get-keyvault-secrets@a30e9c3d658dc97c4c2e61ec749fdab64b83386c - with: - keyvault: "bitwarden-ci" - secrets: "github-pat-bitwarden-devops-bot-repo-scope" - - - name: Setup DCT - if: ${{ env.is_publish_branch == 'true' }} - id: setup-dct - uses: bitwarden/gh-actions/setup-docker-trust@a30e9c3d658dc97c4c2e61ec749fdab64b83386c - with: - azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - azure-keyvault-name: "bitwarden-ci" + - name: Generate image full name + id: image-name + env: + IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} + PROJECT_NAME: ${{ matrix.image_name }} + run: echo "name=$_AZ_REGISTRY/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT - name: Build Docker image uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1 @@ -257,21 +240,10 @@ jobs: file: apps/web/Dockerfile platforms: linux/amd64 push: true - tags: ${{ steps.tag-list.outputs.tags }} + tags: ${{ steps.image-name.outputs.name }} secrets: | "GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" - - name: Push to DockerHub - if: contains(matrix.registries, 'bitwarden') && env.is_publish_branch == 'true' - env: - IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} - PROJECT_NAME: ${{ matrix.image_name }} - DOCKER_CONTENT_TRUST: 1 - DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} - run: | - docker tag bitwardenprod.azurecr.io/$PROJECT_NAME:$IMAGE_TAG bitwarden/$PROJECT_NAME:$IMAGE_TAG - docker push bitwarden/$PROJECT_NAME:$IMAGE_TAG - - name: Log out of Docker run: docker logout @@ -279,8 +251,7 @@ jobs: crowdin-push: name: Crowdin Push if: github.ref == 'refs/heads/master' - needs: - - build-artifacts + needs: build-artifacts runs-on: ubuntu-22.04 steps: - name: Checkout repo diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 0a9a0bcb23f..b53542747d9 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -15,6 +15,9 @@ on: - Redeploy - Dry Run +env: + _AZ_REGISTRY: bitwardenprod.azurecr.io + jobs: setup: name: Setup @@ -46,7 +49,6 @@ jobs: monorepo: true monorepo-project: web - self-host: name: Release self-host docker runs-on: ubuntu-22.04 @@ -67,42 +69,6 @@ jobs: - name: Checkout repo uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - ########## DockerHub ########## - - name: Setup DCT - id: setup-dct - uses: bitwarden/gh-actions/setup-docker-trust@a30e9c3d658dc97c4c2e61ec749fdab64b83386c - with: - azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - azure-keyvault-name: "bitwarden-ci" - - - name: Pull branch image - run: | - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker pull bitwarden/web:latest - else - docker pull bitwarden/web:$_BRANCH_NAME - fi - - - name: Docker Tag version - run: | - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag bitwarden/web:latest bitwarden/web:$_RELEASE_VERSION - else - docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION - fi - - - name: Docker Push version - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - env: - DOCKER_CONTENT_TRUST: 1 - DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} - run: docker push bitwarden/web:$_RELEASE_VERSION - - - name: Log out of Docker and disable Docker Notary - run: | - docker logout - echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV - ########## ACR ########## - name: Login to Azure - PROD Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 @@ -112,28 +78,37 @@ jobs: - name: Login to Azure ACR run: az acr login -n bitwardenprod - - name: Tag version - env: - REGISTRY: bitwardenprod.azurecr.io + - name: Pull branch image run: | if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag bitwarden/web:latest $REGISTRY/web:$_RELEASE_VERSION - - docker tag bitwarden/web:latest $REGISTRY/web-sh:$_RELEASE_VERSION + docker pull $_AZ_REGISTRY/web:latest else - docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web:$_RELEASE_VERSION + docker pull $_AZ_REGISTRY/web:$_BRANCH_NAME + fi - docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:$_RELEASE_VERSION + - name: Tag version + run: | + if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web:dryrun + docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web-sh:dryrun + else + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:$_RELEASE_VERSION + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:latest + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:latest fi - name: Push version - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - env: - REGISTRY: bitwardenprod.azurecr.io run: | - docker push $REGISTRY/web:$_RELEASE_VERSION - - docker push $REGISTRY/web-sh:$_RELEASE_VERSION + if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + docker push $_AZ_REGISTRY/web:dryrun + docker push $_AZ_REGISTRY/web-sh:dryrun + else + docker push $_AZ_REGISTRY/web:$_RELEASE_VERSION + docker push $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION + docker push $_AZ_REGISTRY/web:latest + docker push $_AZ_REGISTRY/web-sh:latest + fi - name: Log out of Docker run: docker logout @@ -190,7 +165,7 @@ jobs: - name: Unzip build asset working-directory: assets run: unzip web-*-cloud-COMMERCIAL.zip - + - name: Create new branch run: | cd ${{ github.workspace }}/ghpages-deployment @@ -199,12 +174,12 @@ jobs: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ git config --global url."https://".insteadOf ssh:// git checkout -b ${_BRANCH} - + - name: Copy build files run: | rm -rf ${{ github.workspace }}/ghpages-deployment/* cp -Rf ${{ github.workspace }}/assets/build/* ghpages-deployment/ - + - name: Commit and push changes working-directory: ghpages-deployment run: | From ffabb64f6dffe2f023b7daf49d5458bf9357a023 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:16:27 -0700 Subject: [PATCH 027/135] [PM-3099] Desktop UI Small Screen Tweaks (#4620) * add responsive queries for small screens * increase media query max-width so icons don't get cut off on medium screens --- apps/desktop/src/scss/box.scss | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/desktop/src/scss/box.scss b/apps/desktop/src/scss/box.scss index 2b720eac69c..9dbdc80a5bf 100644 --- a/apps/desktop/src/scss/box.scss +++ b/apps/desktop/src/scss/box.scss @@ -504,3 +504,26 @@ } } } + +.details { + .inner-content { + .box-content-row-flex:not([type="button"]) { + @media (max-width: 875px) { + flex-direction: column; + align-items: start; + } + + .action-buttons { + @media (max-width: 875px) { + margin-left: 0; + } + } + + .row-btn:first-of-type { + @media (max-width: 875px) { + margin-left: -8px; + } + } + } + } +} From 7dc284bc3ee2346e38324591167fcfb876309e07 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:18:42 -0700 Subject: [PATCH 028/135] remove login CTA and add footer link (#6028) --- .../auth/register-form/register-form.component.html | 11 +++++------ apps/web/src/locales/en/messages.json | 3 +++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/auth/register-form/register-form.component.html b/apps/web/src/app/auth/register-form/register-form.component.html index 44a53fb6e40..9a5220c57d4 100644 --- a/apps/web/src/app/auth/register-form/register-form.component.html +++ b/apps/web/src/app/auth/register-form/register-form.component.html @@ -123,8 +123,7 @@
-
-
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 485b0e616b0..b91ea4add5e 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7094,5 +7094,8 @@ }, "beta": { "message": "Beta" + }, + "alreadyHaveAccount": { + "message": "Already have an account?" } } From acd169b113cfcea946fe34084541c37bc1b34537 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 16 Aug 2023 10:21:51 -0700 Subject: [PATCH 029/135] update link for Web and Browser (#5779) --- .../angular/src/auth/components/two-factor-options.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/angular/src/auth/components/two-factor-options.component.ts b/libs/angular/src/auth/components/two-factor-options.component.ts index a7d8a3235e3..786a9d7bb54 100644 --- a/libs/angular/src/auth/components/two-factor-options.component.ts +++ b/libs/angular/src/auth/components/two-factor-options.component.ts @@ -30,7 +30,7 @@ export class TwoFactorOptionsComponent implements OnInit { } recover() { - this.platformUtilsService.launchUri("https://bitwarden.com/help/lost-two-step-device/"); + this.platformUtilsService.launchUri("https://vault.bitwarden.com/#/recover-2fa"); this.onRecoverSelected.emit(); } } From 70f115c8f5929defb77b800393a8e7404d9a296a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ch=C4=99ci=C5=84ski?= Date: Thu, 17 Aug 2023 10:33:07 +0200 Subject: [PATCH 030/135] [DEVOPS-1438] Migrate our mac os desktop notary tool (#5741) * Change altool to notarytool for desktop app notarizing * Comment for testing * Add team id * Try to notarize with old method * TEst vaslues * Change after-sign notarization option * CHange notarization in package * Fix * fix * Maybe fix * Use altool to upload * Re enable if after testing --- apps/desktop/electron-builder.json | 1 + apps/desktop/scripts/after-sign.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index e92a80c4972..8e6d300218a 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -97,6 +97,7 @@ }, "dmg": { "icon": "dmg.icns", + "sign": false, "contents": [ { "x": 150, diff --git a/apps/desktop/scripts/after-sign.js b/apps/desktop/scripts/after-sign.js index 4ef3023946a..0b181f90f62 100644 --- a/apps/desktop/scripts/after-sign.js +++ b/apps/desktop/scripts/after-sign.js @@ -53,8 +53,9 @@ async function run(context) { const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID; const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`; return await notarize({ - appBundleId: "com.bitwarden.desktop", + tool: "notarytool", appPath: appPath, + teamId: "LTZ2PFU5D6", appleId: appleId, appleIdPassword: appleIdPassword, }); From 1b32cddbad9e797bdefcc28035ae0404d49a3f81 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:47:03 +1000 Subject: [PATCH 031/135] Fix SM beta grace period dates (#6054) --- .../organization-subscription-cloud.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 82da9bda1ff..dedc99edcb1 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 @@ -47,8 +47,8 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy firstLoaded = false; loading: boolean; - private readonly _smBetaEndingDate = new Date(2023, 7, 25); - private readonly _smGracePeriodEndingDate = new Date(2023, 9, 24); + private readonly _smBetaEndingDate = new Date(2023, 7, 15); + private readonly _smGracePeriodEndingDate = new Date(2023, 10, 14); private destroy$ = new Subject(); From e916cec6b514fdd62eae123c70ae629ca6f5b776 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:47:36 +1000 Subject: [PATCH 032/135] [AC-1599] Fix Secrets Manager cost subtotal when creating an organization (#6044) * Fix service account cost not included in SM subtotal * Simplify logic --- .../settings/organization-plans.component.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/web/src/app/billing/settings/organization-plans.component.ts b/apps/web/src/app/billing/settings/organization-plans.component.ts index 7d9f76708ea..5c6359af951 100644 --- a/apps/web/src/app/billing/settings/organization-plans.component.ts +++ b/apps/web/src/app/billing/settings/organization-plans.component.ts @@ -314,16 +314,11 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { return 0; } - let subTotal = plan.basePrice; - if (plan.hasAdditionalSeatsOption && formValues.userSeats) { - subTotal += this.seatTotal(plan, formValues.userSeats); - } - - if (plan.hasAdditionalStorageOption && formValues.additionalServiceAccounts) { - subTotal += this.additionalServiceAccountTotal(this.selectedPlan); - } - - return subTotal; + return ( + plan.basePrice + + this.seatTotal(plan, formValues.userSeats) + + this.additionalServiceAccountTotal(plan) + ); } get freeTrial() { From 56b0fffdc85c566a4596604ff9fd85b81c3a8e00 Mon Sep 17 00:00:00 2001 From: Robyn MacCallum Date: Fri, 18 Aug 2023 11:20:10 -0400 Subject: [PATCH 033/135] Update CODEOWNERS (#6060) * Update CODEOWNERS * Update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 15de72d785b..564763a866d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,7 @@ # The following owners will be the default owners for everything in the repo. # Unless a later match takes precedence -* @bitwarden/team-leads-eng +* @bitwarden/tech-leads ## Secrets Manager team files ## bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manager-dev From 5665576147a4fbf30024f56358d424ff26a1a131 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:05:08 -0400 Subject: [PATCH 034/135] Trusted Device Encryption feature (#5950) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * PM-1049 - Create first display draft of login-decryption-options base and web components (no data loading or user actions wired up yet; WIP) * PM-1049 - Update DeviceResponse to match latest properties on backend * PM-1049 - Add getDevices call to retrieve all user devices to API service * PM-1049 - WIP on figuring out login decryption options component requirements * PM-1049 - Add empty login decryption options to desktop * PM-1049 - Desktop - Update "Log in initiated" translation to be "Login Initiated" per figma and product request * PM-1049 - Desktop - login decryption options component html done * PM-1049 - Move login-decryption-options in web into own folder * PM-1049 - Browser - created html for login-decryption-options component * PM-1049 - Move newly created getDevices() method out of api.service into proper place in new devices-api.service. * PM-1049 -Comment cleanup and TODO added * PM-1049 - Comment cleanup and dependency cleanup across all login-decryption-options comps * PM-1049 - WIP of building out needed response and regular models for saving new UserDecryptionOptions on the Account in state. * PM-1049 - Update all User Decryption Options response and state models in light of the back end changes from a list to an object. Web building now with decryption options stored on state under the account successfully. Must now build out state service methods for retrieving / setting account decryption options for use elsewhere. * PM-1049 - State Service - setup setters / getters for UserDecryptionOptions off the account * PM-1049 - StateService - replace User with Acct for decryption options * PM-1049 - Create domain models vs using response models as response models have a response property w/ the full response nested underneath which we don't need to persist for the user decryption options stored on the account. * PM-1049 - AcctDecryptionOptions now persist across page refreshes of the login-initiated page to act similarly to refreshes on the lock screen. Accomplished via persisting AcctDecryptionOptions in local storage -- still cleared on logout. * PM-1049 - IdTokenResponse - only userDecryptionOptions if they exist on the response from the server; I saw a few instances where it did not. Wasn't able to replicate consistently, but I put this check here to be safe. * PM-1049 - Login Initiated route can only be accessed if user is AuthN w/ locked vault + TDE feature flag is on. * PM-1049 - LoginDecryptionOptions - (1) Wire up loading logic (2) Retrieve User Acct Decryption options to determine whether or not to show request admin approval btn and approve w/ MP (3) Write up future logic for requestAdminApproval (4) approveWithMasterPassword takes you to the lock screen to login. * PM-1049 - Apply same guards as in web to login-decryption-options in desktop & browser. * PM-1049 - (1) Updated dependencies in parent BaseLoginDecryptionOptionsComponent class + child components (2) Retrieve userEmail b/c needed for displaying which email the user is logging in with (3) Add log out functionality (4) Add comments regarding future implementation details for each login approval flow. * PM-1049 - Web/Browser/Desktop LoginDecryptionOptions - (1) Wire up approval buttons (2) Add conditional margins (3) Loading spinner added (4) Display userEmail + "not you" logout link * PM-1049 - Add TODOs for future changes needed as part of the Login Approval flows for TDE * PM-1049 - TODO: replace base component with business service * add new storage to replace MasterKey with UserSymKey * add storage for master key encrypted user symmetric key * Begin refactor of crypto service to support new key structure * remove provided key from getKeyForUserEncryption * add decryption with MasterKey method to crypto service * update makeKeyPair on crypto service to be generic * add type to parameter of setUserKey in abstraction of crypto service * add setUserSymKeyMasterKey so we can set the encrypted user sym key from server * update cli with new crypto service methods - decrypt user sym key and set when unlocking * separate the user key in memory from user keys in storage * add new memory concept to crypto service calls in cli * update auth service to use new crypto service * update register component in lib to use new crypto service * update register component again with more crypto service * update sync service to use new crypto service methods * update send service to use new crypto service methods * update folder service to use new crypto service methods * update cipher service to use new crypto service * update password generation service to use new crypto service * update vault timeout service with new crypto service * update collection service to use new crypto service * update emergency access components to use new crypto service methods * migrate login strategies to new key model - decrypt and set user symmetric key if Master Key is available - rename keys where applicable - update unit tests * migrate pin to use user's symmetric key instead of master key - set up new state - migrate on lock component - use new crypto service methods * update pin key when the user symmetric key is set - always set the protected pin so we can recreate pin key from user symmetric key - stop using EncryptionPair in account - use EncString for both pin key storage - update migration from old strategy on lock component * set user symmetric key on lock component - add missed key suffix types to crypto service methods * migrate auto key - add helper to internal crypto service method to migrate * remove additional keys in state service clean * clean up the old pin keys in more flows - in the case that the app is updated while logged in and the user changes their pin, this will clear the old pin keys * finish migrate auto key if needed - migrate whenever retrieved from storage - add back the user symmetric key toggle * migrate biometrics key - migrate only on retrieval * fix crypto calls for key connector and vault timeout settings * update change password components with new crypto service * update assortment of leftover old crypto service calls * update device-crypto service with new crypto service * remove old EncKey methods from crypto service * remove clearEncKey from crypto service * move crypto service jsdoc to abstraction * add org key type and new method to build a data enc key for orgs * fix typing of bulk confirm component * fix EncString serialization issues & various fixes Co-authored-by: Matt Gibson * update account model with new keys serialization * migrate native messaging for biometrics to use new key model - support backwards compatibility - update safari web extension to send user key - add error handling * add early exit to native messaging flow for errors * improve error strings in crypto service * disable disk cache for browser due to bg script/popup race conditions * clear bio key when pin is migrated as bio is refreshed * share disk cache to fix syncing issues between contexts * check for ephemeral pin before process reload * remove state no longer needed and add JSDOC * fix linter * add new types to tests * remove cryptoMasterKeyB64 from account * fix tests imports * use master key for device approvals still * cleanup old TODOs, add missing crypto service parameters * fix cli crypto service calls * share disk cache between contexts on browser * Revert "share disk cache between contexts on browser" This reverts commit 56a590c4919f119cb1465eb7091a4384f5d90699. * use user sym key for account changing unlock verification * add tests to crypto service * rename 'user symmetric key' with 'user key' * remove userId from browser crypto service * updated EncKey to UserKey where applicable * jsdoc deprecate account properties * use encrypt service in crypto service * use encrypt service in crypto service * require key in validateUserKey * check storage for user key if missing in memory * change isPinLockSet to union type * move biometric check to electron crypto service * add secondary fallback name for bio key for safari * migrate master key if found * pass key to encrypt service * rename pinLock to pinEnabled * use org key or user key for encrypting attachments * refactor makeShareKey to be more clear its for orgs * rename retrieveUserKeyFromStorage * clear deprecated keys when setting new user key * fix cipher service test * options is nullable while setting user key * more crypto service refactors - check for auto key when getting user key - consolidate getUserKeyFromMemory and FromStorage methods - move bio key references out of base crypto service - update either pin key when setting user key instead of lock component - group deprecated methods - rename key legacy method * Feature/PM-1049 - TDEFflow 3 login decryption options - PR feedback changes (#5642) * PM-1049 - PR Feedback change - Browser - replace incorrect use of routerlink with manual attribute styling to keep anchor styling + tab focus while not having a router action race condition for the log out action to complete. * PM-1049 - PR Feedback - State Service changes - rename get/setAcctDecryptionOptions to get/setAccountDecryptionOptions * PM-1049 - PR Feedback changes - LoginDecryptionOptionsComp - Remove unncessary appA11yTitle directives as title / aria text would be identical to the displayed inner button text. * DeviceType - Create sets of device types which other components can reference to avoid having to manually define groups of device types. * PM-1049 - PR Feedback Changes - Update base-login-decryption-options component to leverage async piped observables per best practices. Updated all client templates to leverage new data streams. * PM-1049 - BaseLoginDecryptionOptionsComp - Add validation service for generic error handling * PM-1049 - DeviceResponse mistakenly had name as a number instead of a string * PM-1049 - First draft of creating observable based data store service for Devices so that the base login comp can leverage it instead of calling the devices API service directly (as it will be moved into the SDK in the future). * PM-1049 - Register new DevicesService on jslib-services module for use in components. * PM-1049 - Add new hasDevicesOfTypes call to devices data store svc + devices API service. * PM-1049 - BaseLoginDecryptionOptionsComp - wire up call to devicesService.hasDevicesOfTypes to replace getDevices() to avoid bringing down all trusted device information unnecessarily. * PM-1049 - LoginDecryptionOptionsComp - Web HTML - clean up loading state so it displays spinner centered properly. * PM-1049 - LoginDecryptionOptionsComp - Desktop HTML - Don't show login initiated title while page is loading to match other clients behavior. * PM-1049 - Devices Services - Update naming of hasDevicesOfTypes to match new name on back end + route change to getDevicesExistenseByTypes * PM-1049 - Device Response & View models - remove keys which are going to be deprecated on the base model * PM-1049 - DevicesService - devicesBSubject --> devicesSubject rename per PR feedback * PM-1049 - Devices Services - correct spelling of existence (*facepalm*) * PM-1049 - Update comment for clarity per PR feedback * PM-1049 - DevicesSvc - UserSymKey --> UserKey rename * PM-1049 - BaseLoginDecryptionOptions - replace user email source - get from stateService vs tokenService. * PM-1049 - BaseLoginDecryptionOptions - Remove uncessary check for userEmail as we will always have it here otherwise everything in the app is broken. * PM-1049 - BaseLoginDecryptionOptions - Finish cleaning up removal of user email from showReqAdminApprovalBtn$ stream * PM-1049 - LoginDecryptionOptionsComp - HTML revisions in web & browser to better space out buttons using tailwind or top margin to avoid need for multiple async pipes and shareReplay. * PM-1049 - DevicesService - of course all observables should have $ suffix. Facepalm. * PM-1049 - BaseLoginDecryptionOptionsComp - Update verbiage and style of destroy observable used for hooking into ngOnDestroy lifecycle to clean up all observables * PM-1049 - BaseLoginDecryptionOptions - PR feedback changes - refactor user email to have an underlying bSubject stream to ensure subscription/promise execution separately from the template async pipe subscribing to the stream. * PM-1049 - DevicesApiService - getDevicesExistenceByTypes - PR feedback - explicitly convert result to boolean instead of casting. * PM-1049 - BaseLoginDecryptionOptionsComp - Add ShareReplay for getAccountDecryptionOptions + context per PR feedback * PM-1049 - LoginDecryptionOptionsComp - Completely back away from template async pipe reactive approach as it caused massively increased complexity for little gain. Instead, just focus on reactively pulling asynchronously retrieved data and setting page loading state simply. This just works and is so much less overhead. + Add comments re flows of the component to be done later * PM-1049- Revert DevicesService implementation from smart data store cache service giant mess into simple, clean data passthrough service to avoid complexity and keep moving forward. YAGNI Co-authored-by: Andreas Coroiu * PM-1049 - DeviceCryptoService - Add decryptUserKey method (WIP) * PM-1049 - AccountDecryptionOptions - add get helpers for checking for trusted device / key connector decryption option existence. * PM-1049 - SSO Login Strategy - added comments in setUserKey method for where we will probably be consuming device keys and determining if the device is trusted or not (i.e., if we can get a decrypted user sym key in memory) * PM-1049 - DeviceCryptoSvc.decryptUserKey - Update method to properly use state service device key retrieval + add TODO to figure out what to do if user has previously had a device key and has cleared their local cache (which will result in the device being untrusted now) * PM-1049 - SSO Login Strategy - add comment re future passkey login strategy support * PM-2759 - SSO & 2FA components updated with v0 of navigation logic to send users to LoginDecryptionOptions * PM-1049 - Account > AccountDecryptionOptions - can't create getter helper methods for determining if user has decryption options b/c of issues w/ account deserialization. Moving past b/c I can just easily check if the given options are not undefined. * PM-2759 - Add TODOs for deprecation of id token response resetMasterPassword logic and replacement with use of accountDecryptionOptions --------- Co-authored-by: Andreas Coroiu * revert sharing disk cache between contexts * fix tests * add better tests to crypto service * add hack to get around duplicate instances of disk cache on browser * prevent duplicate cache deletes in browser * fix browser state service tests * Feature/PM-1212 - TDE - Approve with master password flow (#5706) * PM-1212 - StateSvc - Add getUserDeviceTrustChoice && setUserDeviceTrustChoice to persist user's choice in local storage in case of refresh on login approval screens (ex: lock) * PM-1212 - DeviceCryptoSvc - Add getUserDeviceTrustChoice && setUserDeviceTrustChoice as state service is lower level service for caching * PM-1212 - LoginDecryptionOptionsComp - Save result of rememberEmail checkbox into local storage via deviceCryptoService.setUserDeviceTrustChoice * PM-1212 - Lock component - after user key is set, check if user chose to establish trust, and if they did, then establish trust and reset choice. * PM-1212 - Update naming of methods per discussion with Jake + add comment explaining intended single use retrieval and need for resetting the value. * DeviceCryptoService - Refactor - decryptUserKey --> decryptUserKeyWithDeviceKey to match crypto service refactor naming convention * PM-1212 - Refactor State Service per PR feedback to store trustDeviceChoiceForDecryption on Account.settings b/c the temp setting is scoped to a user. * PM-2759 - SSO & 2FA Navigation to TDE Comp - Needs more work - Found scenarios on web with 2FA in which the expected navigation doesn't work. Adding TODO to assist in fixing * (1) Add Trust to DeviceCryptoService name (2) Move DeviceTrustCryptoService under auth folder * PM-1212 - Add tests for new getUserTrustDeviceChoiceForDecryption and setUserTrustDeviceChoiceForDecryption methods + TODOs for future tests. * PM-1212- Renaming / moving DeviceTrustCryptoService broke all the things - fixed all the client builds. * PM-1212- Copy doc comment to abstraction per PR feedback * PM-1212 - BaseLoginDecryptionOptions comp - remove unncessary cast to form control as apparently reactive forms now properly derives types. * [PM-1203] Replace MP confirmation with verification code (#5656) * [PM-1203] feat: ask for OTP if user does not have MP * [PM-1203] feat: add backwards compatibility for accounts/servers without decryption options * [PM-1203] feat: move hasMasterPassword to user-verification.service * [PM-1203] fix: remove duplicate implementation from crypto service * [PM-1203] fix: cli build * Tweak device trust crypto service implementation to match mobile late… (#5744) * Tweak device trust crypto service implementation to match mobile latest which results in more single responsibility methods * Update tests to match device trust crypto service implementation changes * update comment about state service * update pinLockType states and add jsdocs * add missed pinLockType changes * [PM-1033] Org invite user creation flow 1 (#5611) * [PM-1033] feat: basic redirection to login initiated * [PM-1033] feat: add ui for TDE enrollment * [PM-1033] feat: implement auto-enroll * [PM-1033] chore: add todo * [PM-1033] feat: add support in browser * [PM-1033] feat: add support for desktop * [PM-1033] feat: improve key check hack to allow regular accounts * [PM-1033] feat: init asymmetric account keys * [PM-1033] chore: temporary fix bug from merge * [PM-1033] feat: properly check if user can go ahead an auto-enroll * [PM-1033] feat: simplify approval required * [PM-1033] feat: rewrite using discrete states * [PM-1033] fix: clean-up and fix merge artifacts * [PM-1033] chore: clean up empty ng-container * [PM-1033] fix: new user identification logic * [PM-1033] feat: optimize data fetching * [PM-1033] feat: split user creating and reset enrollment * [PM-1033] fix: add missing loading false statement * [PM-1033] fix: navigation logic in sso component * [PM-1033] fix: add missing query param * [PM-1033] chore: rename to `ExistingUserUntrustedDevice` * PM-1033 - fix component templates to reference `ExistingUserUntrustedDevice` so clients can build --------- Co-authored-by: Jared Snider * remove extra partial key * set master key on lock component * rename key hash to password hash on crypto service * fix cli * rename enc user key setter in crypto service * Adds Events & Human Readable Messages (#5746) * [PM-1202] Hide the Master Password tab on Settings / Security (#5649) * [PM-1203] feat: ask for OTP if user does not have MP * [PM-1203] feat: get master password status from decryption options * [PM-1203] feat: add backwards compatibility for accounts/servers without decryption options * [PM-1203] feat: move hasMasterPassword to user-verification.service * fix merge issues * Change getUserTrustDeviceChoiceForDecryption / setUserTrustDeviceChoiceForDecryption to getShouldTrustDevice / setShouldTrustDevice (#5795) * Auth/[PM-1260] - Existing User - Login with Trusted Device (Flow 2) (#5775) * PM-1378 - Refactor - StateSvc.getDeviceKey() must actually convert JSON obj into instance of SymmetricCryptoKey * TODO: BaseLoginDecryptionOptionsComponent - verify new user check doesn't improperly pick up key connector users * PM-1260 - Add new encrypted keys to TrustedDeviceUserDecryptionOptionResponse * PM-1260 - DeviceTrustCryptoSvc - decryptUserKeyWithDeviceKey: (1) update method to optionally accept deviceKey (2) Return null user key when no device key exists (3) decryption of user key now works in the happy path * PM-1260 - LoginStrategy - SaveAcctInfo - Must persist device key on new account entity created from IdTokenResponse for TDE to work * PM-1260 - SSO Login Strategy - setUserKey refactor - (1) Refactor existing logic into trySetUserKeyForKeyConnector + setUserKeyMasterKey call and (2) new trySetUserKeyWithDeviceKey method for TDE * PM-1260 - Refactor DeviceTrustCryptoService.decryptUserKeyWithDeviceKey(...) - Add try catch around decryption attempts which removes device key (and trust) on decryption failure + warn. * PM-1260 - Account - Add deviceKey to fromJSON * TODO: add device key tests to account keys * TODO: figure out state service issues with getDeviceKey or if they are an issue w/ the account deserialization as a whole * PM-1260 - Add test suite for decryptUserKeyWithDeviceKey * PM-1260 - Add interfaces for server responses for UserDecryptionOptions to make testing easier without having to use the dreaded any type. * PM-1260 - SSOLoginStrategy - SetUserKey - Add check looking for key connector url on user decryption options + comment about future deprecation of tokenResponse.keyConnectorUrl * PM-1260 - SSO Login Strategy Spec file - Add test suite for TDE set user key logic * PM-1260 - BaseLoginStrategy - add test to verify device key persists on login * PM-1260 - StateService - verified that settings persist properly post SSO and it's just device keys we must manually instantiate into SymmetricCryptoKeys * PM-1260 - Remove comment about being unable to feature flag auth service / login strategy code due to circ deps as we don't need to worry about it b/c of the way we've written the new logic to be additive. * PM-1260 - DevicesApiServiceImplementation - Update constructor to properly use abstraction for API service * PM-1260 - Browser - AuthService - (1) Add new, required service factories for auth svc and (2) Update auth svc creation in main.background with new deps * PM-1260 - CLI - Update AuthSvc deps * PM-1260 - Address PR feedback to add clarity / match conventions * PM-1260 - Resolving more minor PR feedback * PM-1260 - DeviceTrustCryptoService - remove debug warn * PM-1378 - DeviceTrustCryptoSvc - TrustDevice - Fix bug where we only partially encrypted the user key with the device public key b/c I incorrectly passed userKey.encKey (32 bytes) instead of userKey.key (64 bytes) to the rsaEncrypt function which lead to an encryption type mismatch when decrypting the user's private key with the 32 byte decrypted user key obtained after TDE login. (Updated happy path test to prevent this from happening again) * PM-1260 - AccountKeys tests - add tests for deviceKey persistence and deserialization * PM-1260 - DeviceTrustCryptoSvc Test - tweak verbiage per feedback * PM-1260 - DeviceTrustCryptoSvc - Test verbiage tweak part 2 * Update apps/browser/src/background/service-factories/devices-api-service.factory.ts per PR feedback Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * Defect - LockComp - After setting user key, must AWAIT retrieval of user's previous choice to have trusted the device or not. (#5804) * [PM-2928] [PM-2929] [PM-2930] Fixes for: [PM-1203] Replace MP confirmation with verification code (#5798) * [PM-2928] feat: hide change email if user doen't have MP * [PM-2929] feat: hide KDF settings if user doesn't have MP * [PM-2930] feat: remove MP copy * Removed self-hosted check from TDE SSO config. (#5837) * [PM-2998] Move Approving Device Check (#5822) * Switch to retrieving approving device from token response - Remove exist-by-types API call - Define `HasApprovingDevices` on TDE options * Update Naming * Update Test * Update Missing Names * [PM-2908] feat: show account created toast (#5810) * fix bug where we weren't passing MP on Restart to migrate method in lock * fix: buffer null error (#5856) * Auth/[pm-2759] - TDE - SSO and 2FA routing logic (#5829) * PM-2759 - SsoComp - (1) Temp remove all TDE routing logic (2) Refactor existing navigation logic via new component utility function navigateViaCallbackOrRoute * PM-2759 - SSO Component - Create test suite for logIn logic * PM-2759 - SsoComp Tests - add disclaimer regarding testing private methods and props * PM-1259 - SSO Comp - Refactor LogIn method to use functions for each navigation case for improved readability * PM-1259 - SSO Comp Tests - Add tests for error case during login + test for new handleLoginError logic * PM-2759 - SsoComp - Deprecate resetMasterPassword and replace with AccountDecryptionOptions logic + update tests * PM-2759 - SsoComp + tests - Add trusted device encryption first draft handling which has login success and force password reset handling * PM-2759 - Minor SsoComp comment and method name tweaks * PM-2759 - BaseTwoFactorComp - (1) Comment out TDE stuff for now (2) Add test suite (3) Replace global window in base comp constructor with angular injection token for window which follows best practices and allows for mocking so the comp can be unit tested * PM-2759 - Update child 2FA components to use angular injection token for window like base comp * PM-2759 - TwoFactorComp - Finish testing all logic in doSubmit * PM-2759 - TwoFactorComponent - Refactor DoSubmit method logic into multiple simple functions to make logic easier to follow * PM-2759 - Add newtrustedDeviceOption.hasManageResetPasswordPermission property to match server changes * PM-2759 - Flag AuthResult.resetMasterPassword property as deprecated * PM-2759 - SSO comp - TDE routing logic - User without MP and ResetPassword permission must set a MP * PM-2759 - Update Sso Comp tests to reflect additionally added TDE > MP set required logic (when user has no MP but they can reset other user passwords) * PM-2759 - SsoComp - Add comment explaining the happy paths better for TDE success navigation * PM-2759 - SsoComp - Refactor isTrustedDeviceEncEnabled logic into own method * PM-2759 - SsoComp - As the 2FA comp passes the org id through to each route, going to standardize on doing so across the board for now to avoid any tricky scenarios down the line where it is needed and it's not present * PM-2759 - SsoComp - Finish renaming orgIdFromState to orgIdentifier * PM-2759 - SsoComp - update tests for forcePasswordReset flows now passing orgIdentifier as query param * PM-2759 - SsoComp Tests - Export mockAcctDecryptionOpts permutations so we can share them across SsoComp and TwoFactorComp tests * PM-2759 - Refactor 2FA comp post login redirect logic to match SSO component + add TDE logic * PM-2759 - SsoComp - Refactor tests a bit for improved re-use * PM-2759 - Sso Comp tests - can't export consts from a spec file or the other spec files that import them will re-execute the whole test suite as a nested test suite. TIL. * PM-2759 - TwoFactorComp tests - All existing navigation scenarios + new TDE scenarios should now be tested. * PM-2759 - Web - 2FA comp - Fix build error b/c of renamed base comp prop (identifier --> orgIdentifier) * PM-2759 - Fix SsoLogin strategy tests b/c they were broken w/ the addition of the HasManageResetPasswordPermission prop to the TrustedDeviceOption interface * PM-2759 - Web TwoFactorComp - goAfterLogIn method must be an arrow function to inherit the parent base component scope so that important things like angular services can be defined. Web 2FA flow does not work without this being an arrow func. * PM-2759 - Fix typo * PM-2759 - SsoComp and TwoFactorComp tests - move service and other mocks into the top level before each to better ensure no crossover between test states per PR feedback * PM-2759 - SsoComp - add clarity by refactoring unclear comment * PM-2759 - SsoComp - Per excellent PR feedback, refactor if else statements to guard statements for better readability / design * PM-2759 - TwoFactorComp - Replace ifs with guard statements * PM-2759 - TwoFactorComp - add clarity to comment per PR feedback * PM-2759 - Replace use of jest.Mocked with MockProxy per PR feedback * PM-2759 - Use unknown over any per PR feedback * Bypass Master Password Reprompt if a user does not have a MP set (#5600) * Add a check for a master password in PasswordRepromptService.enabled() * Add tests for enabled() * Update state service method call * Use UserVerificationService to determine if a user has a master password * rename password hash to master key hash * fix cli build from key hash renaming * [PM-1339] Allow Rotating Device Keys (#5806) * Merge remote-tracking branch 'origin/feature/trusted-device-encryption' into Auth/pm-1339/rotate-device-keys * Implement Rotation of Current Device Keys - Detects if you are on a trusted device - Will rotate your keys of only this device - Allows you to still log in through SSO and decrypt your vault because the device is still trusted * Address PR Feedback * Move Files to Auth Ownership * fix: getOrgKeys returning null * [PM-3143] Trusted device encryption: Refactor reset enroll service (#5869) * create new reset enrollment service * refactor: login decryption options according to TODO * feat: add tests * PM-3143 - Add override to overriden methods --------- Co-authored-by: Jared Snider * generate a master key from master password if needed (#5870) * [PM-3120] fix: device key not being saved properly (#5882) * Auth/pm 1050/pm 1051/remaining tde approval flows (#5864) * fix: remove `Unauth guard` from `/login-with-device` * [PM-3101] Fix autofill items not working for users without a master password (#5885) * Add service factories for user verification services * Update autofill service to check for existence of master password for autofill * Update the context menu to check for existence of master password for autofill * context menu test fixes * [PM-3210] fix: use back navigation (#5907) * Removed buttons (#5935) * PM-2759 - Fix broken backwards compatibility for authResult.resetMast… (#5940) * PM-2759 - Fix broken backwards compatibility for authResult.resetMasterPassword * PM-2759 - Update TODO with specific tech debt task + target release date * TDE - State Svc - setDeviceKey should support setting null for future support of clearing device key. (#5942) * Check if a user has a mp before showing kdf warning (#5929) * [PM-1200] Unlock settings changes for accounts without master password - clients (#5894) * [PM-1200] chore: add comment for jake * [PM-1200] chore: rename to `vault-timeout` * [PM-1200] feat: initial version of `getAvailableVaultTimeoutActions` * [PM-1200] feat: implement `getAvailableVaultTimeoutActions` * [PM-1200] feat: change helper text if only logout is available * [PM-1200] feat: only show available timeout actions * [PM-1200] fix: add new service factories and dependencies * [PM-1200] fix: order of dependencies `UserVerificationService` is needed by `VaultTimeoutSettingsService` * [PM-1200] feat: add helper text if no lock method added * [PM-1200] refactor: simplify prev/new values when changing timeout and action * [PM-1200] feat: fetch timeout action from new observable * [PM-1200] refactor: make `getAvailableVaultTimeoutActions` private * [PM-1200] feat: add test cases for `vaultTimeoutAction$` * [PM-1200] feat: implement new timeout action logic * [PM-1200] feat: add dynamic lock options to browser * [PM-1200] feat: enable/disable action select * [PM-1200] feat: add support for biometrics * [PM-1200] feat: add helper text and disable unavailable options * [PM-1200] feat: update action on unlock method changes * [PM-1200] feat: update browser to use async pipe * [PM-1200] fix: element not updating * [PM-1200] feat: hide masterPassOnRestart pin option * [PM-1200] feat: hide change master password from browser settins * [PM-1200] feat: hide change master password from app menu * [PM-1200] feat: logout if lock is not supported * [PM-1200] feat: auto logout from lock screen if unlocking is not supported * [PM-1200] feat: remove lock button from web menus * Revert "[PM-1200] fix: element not updating" This reverts commit b27f425f48570d0d5dbc9dedb9797023fef64d8b. * Revert "[PM-1200] feat: update browser to use async pipe" This reverts commit 766c15bc3dbadcf7dcef3053b148e7874f8939ce. * [PM-1200] chore: add comment regarding detectorRef * [PM-1200] feat: remove lock now button from browser settings * [PM-1200] feat: add `userId` to unlock settings related methods * [PM-1200] feat: remove non-lockable accounts from menu * [PM-1200] fix: cli not building --------- Co-authored-by: Todd Martin Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> * [PM-3215][PM-3289] Create MasterKey from Password If Needed (#5931) * Create MasterKey from Password - Check if the MasterKey is stored or not - Create it if it's not * Add getOrDeriveKey Helper * Use Helper In More Places * Changed settings menu to be enabled whenever the account is not locked. (#5965) * [PM-3169] Login decryption options in extension popup (#5909) * [PM-3169] refactor: lock guard and add new redirect guard * [PM-3169] feat: implement fully rewritten routing * [PM-3169] feat: close SSO window * [PM-3169] feat: store sso org identifier in state * [PM-3169] fix: tests * [PM-3169] feat: get rid of unconventional patch method * PM-3169 - SSO & 2FA Comps - Update naming of new callback to match existing pattern + add tests for callback logic execution. * PM-3169 - Update LockGuard to have a special exception for allowing the TDE Login with MP flow * PM-3169 - Per discussion w/ Jake and Justin, rename login-initiated guard to be tde decryption required guard (more named for functionality vs specific route) * PM-3169 - Add some additional context to new redirect guard scenario * PM-3169 - Per PR feedback, replace all callback types with Promise as the return values are not being used. * PM-3169 - StateSvc - Per PR feedback, update setUserSsoOrganizationIdentifier signature to explicitly use null instead of partial which doesn't do anything * PM-3169 - Replace onSuccessfulLogin type to compile * PM-3169 - Add clarification comment for why we are not using a query param for persisting the org identifier * PM-3169 - Per discussion with Justin, only use memory for SsoOrgId as we don't need to persist it beyond that; tested and it worked on all 3 clients for new user TDE creation * PM-3169 - Add missing ssoIdentifierRequired translation to desktop and browser * PM-3169 - After discussing with Justin again, we realized that memory doesn't work on desktop if user refreshes app or closes and re-opens it so must use disk. * PM-3169 - Per PR feedback, remove hasEverHadUserKey logic as we can just leverage existing getUserKey method to check if we have a user key or not; tested all guards in browser and web with no issues * PM-3169 - Per design discussion with Danielle, move account created toast after successful account creation vs on load of page. --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider * [PM-3314] Fixed missing MP prompt on lock component (#5966) * Updated lock component to handle no master password. * Added a comment. * Add Missing Slash (#5967) * Fix AdminAuthRequest Serialization on Desktop (#5970) - toJSON isn't being called by ElectronStorageService - Force it's conversion to JSON earlier so it happens for all storage methods * Fix issue where we were incorrectly calling setRememberEmailValues in the AdminAuthRequest state - no need to do this as the email is already saved to state. By calling this method, we would actually overwrite the already saved email with null as the user's choice to remember email wasn't persisted through SSO on the login service. (#5972) * PM-3329 - Restore everHadUserKey logic from PM-3169 which I incorrectly removed in order to fix routing logic so that user can lock and land on the lock screen properly (#5979) * PM-3210 - TDE - LoginWithDevice routing fix - Mirror PR #5950 in just simply providing a back action on click which works for all app generated scenarios (#5982) * PM-3332 - TDE - SsoLoginStrategy - For existing admin auth reqs, must… (#5980) * PM-3332 - TDE - SsoLoginStrategy - For existing admin auth reqs, must manually handle 404 error case to prevent app from hanging and clear the local state if the admin auth req in the DB has been purged; i.e., it should fail silently. * Add TODO for SSO Login Strategy tests * PM-3331 - TDE - Firefox - Browser extension - fix access denied error… (#5984) * PM-3331 - TDE - Firefox - Browser extension - fix access denied error on popup load which was caused by the canAccessFeature guard failing to lookup the TDE feature flag as the server config was returning null even after a successful server call as only returned the value if the user was unauthenticated for some reason * PM-3331 - After discussion with Andre, further refactor ConfigService logic to always return the latest information from the server so that requests for feature flag data will always get the most up to date information. * PM-3345 - TDE - Desktop - Biometrics setting submenu tweak - do not s… (#5988) * PM-3345 - TDE - Desktop - Biometrics setting submenu tweak - do not show require MP or PIN entry on restart if user doesn't have at least one of those options b/c otherwise user can get into a bad state where they cannot unlock * PM-3345 - TDE - Desktop - Settings comp - if user turns off PIN and Biometric is on + require PIN on restart is enabled then must turn that setting off to prevent bad user state * PM-3345 - Final tweak to logic * [PM-2852] Final merge from Key Migration branch to TDE Feature Branch (#5977) * [PM-3121] Added new copy with exclamation mark * [PM 3219] Fix key migration locking up the Desktop app (#5990) * Only check to migrate key on VaultTimeout startup * Remove desktop specific check * PM-3332 - LoginWithDevice - Add error handling logic around admin auth request retrieval similar to sso login strategy to prevent error state and allow re-creation of an admin auth request if it has been purged from the server for whatever reason. (#5991) * PM-3355 - TDE - Browser JIT Account Creation - Browser create user logic still had logic for simply closing the extension tab but as we no longer open the login decryption options in a tab we needed to update the logic here to navigate the user directly onto the vault. (#5993) * Add distinctUntilChanged to fix multiple value changes for biometrics firing (#5999) * Add optional chaining to master key (#6007) * PM-3369 - TDE - Persist user's choice to trust device to state when user ma… (#6000) * PM-3369 - Persist user's choice to trust device to state when user makes choice + persist previous choices out of state * PM-3369 - Must set trust device in state on load if it's never been set before * PM-3369 - Refactor BaseLoginDecOptions to properly set trust device choice in state on load * Update libs/angular/src/auth/components/base-login-decryption-options.component.ts Co-authored-by: Jake Fink --------- Co-authored-by: Jake Fink * Updated email change component to getOrDeriveMasterKey (#6009) * [PM-3330] Force Update to Lockable Accounts on PIN/Biometric Update (#6006) * Add Listener For Events that Need To Redraw the Menu * Send redrawMenu Message When Pin/Biometrics Updated * DeviceTrustCryptoService - don't worry about checking if a device should establish trust or not if the user doesn't have trusted device encryption on (#6010) * Auth / pm 3351 / TDE Login - Browser & Desktop vault sync issue fix (#6002) * PM-3351 - TDE Login on desktop and browser via SSO comp with no 2FA should trigger sync like standard onSuccessfulLogin process used to so user lands on vault with data. * PM-3351 - 2FA Comp - Refactor onSuccessfulLogin logic to only execute in the success path just like the SSO component + adding specific onSuccessfulLoginTde flow just like SSO comp. + removed unnecessary calls to loginService.clearValues(). Added browser & desktop definitions for onSuccessfulLoginTde which is just a fullSync kick off. * TODO * PM-3351 - remove await to restore code back to previous state without hang. * PM-3351 - 2FA Comp - Don't await onSuccessfulLoginTde b/c it causes a hang * PM-3351 - remove sso comp incorrect todo * PM-3351 - SsoComp - don't await onSuccessfulLoginTde for browsers sake * PM-3351 - SsoComp - remove awaits from onSuccessfulLoginTde and onSuccessfulLogin to avoid any hangs on desktop and browser * PM-3351 - Convert onSuccessfulLoginTde to promise as its return is not used + refactor all to be consistent and clearly communciate that the sync won't be awaited. * PM-3351 - Convert onSuccessfulLogin to promise and update all methods accordingly to more clearly indicate that the syncs and any other logic won't be awaited. * [PM-3356] Fallback to OTP When MasterPassword Hasn't Been Used (#6017) * Fallback to OTP When MasterPassword Hasn't Been Used * Update Test and Rename Method * Revert "DeviceTrustCryptoService - don't worry about checking if a device should establish trust or not if the user doesn't have trusted device encryption on (#6010)" (#6020) This reverts commit 6ec22f95702050c12716f79c7d7454835f9b2807. * PM-3390 - TDE - Redraw desktop after user creation to update isLocked checks and get menu to be enabled properly (#6018) * [PM-3383] Hide Change Password menu option for user with no MP (#6022) * Hide Change Master Password menu item on desktop when a user doesn't have a master password. * Renamed variable for consistency. * Updated to base logic on account. * Fixed menubar * Resolve merge errors in crypto service spec * Fixed autofill to use new method on userVerificationService (#6029) * PM-3456 - TDE Admin Auth Req Flow - FF dead object issue - The foreground popup must retrieve the long lived background services for the new TDE services (the AuthRequestCryptoService service fixes this issue, but the DeviceTrustCryptoService should have been added to services.module as well) (#6037) * skip auto key check when using biometrics on browser (#6041) * Added comments for backward compatibility removal. (#6039) * Updated warning message. (#6059) * Tde pr feedback (#6051) * move pin migration to the crypto service * refactor config service logic * refactor lock component load logic * rename key connector methods * add date to backwards compat todo * update backwards compat todo * don't specify defaults in redirectGuard * nit * add null & undefined check for userid before using the account * fix ui tests * add todo for tech debt * add todo comment * Fix storybook per PR feedback * Desktop & Browser - lock comp - add optional chaining check for focusable input - user can just have biometric and not have a MP or a PIN so must support that. * Main.background.ts - remove duplicate instantiations of the userVerificationApiService and userVerificationService which were added in two separate PRs * Per PR feedback - (1) Browser app routing module - fix incorrect import for redirect guard (2) Created index.ts file for auth guards to simplify imports and updated imports * Per PR feedback, (1) Update jslib-services.module to provide actual instance of VaultTimeoutService (2) Update init service to use concrete VaultTimeoutService vs abstraction. Co-authored-by: Matt Gibson * Per PR feedback - update services module AuthRequestCryptoService and DeviceTrustCryptoService to use shorthand format. * Per PR feedback, add devicesService to main background and update services module to ensure the popup leverages the background devicesService --------- Co-authored-by: Jared Snider Co-authored-by: Matt Gibson * Updated message keys for CrowdIn to pick them up. (#6066) * TDE PR Feedback resolutions round 2 (#6068) * Per PR feedback - main.background.ts - move userVerificationService and userVerificationApiService to correct location * Per PR feedback - JS lib services + vault timeout service updates - (1) Correctly type callbacks based on injection tokens (2) Update vault timeout service to have proper types based on injection tokens * Per PR Feedback - update web init service to inject actual VaultTimeoutService vs abstraction similar to what we did for desktop here: https://github.com/bitwarden/clients/commit/55a797d4ff571a1942686a32fdcbb5ad0311b5ae * Per more feedback - revert incorrect changes to VaultTimeoutService based on existing injection token types for LOGOUT_CALLBACK and LOCKED_CALLBACK.. and instead update the injection token types themselves to match how they are being used. * Per PR feedback - in browser main.background.ts, inject concrete VaultTimeoutService instead of abstraction so we don't have to cast it anymore (matching web & desktop) --------- Co-authored-by: Jared Snider Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jacob Fink Co-authored-by: Matt Gibson Co-authored-by: Andreas Coroiu Co-authored-by: Andreas Coroiu Co-authored-by: Andreas Coroiu Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: André Bispo Co-authored-by: Thomas Rittson Co-authored-by: Vincent Salucci Co-authored-by: Robyn MacCallum Co-authored-by: Jonathan Prusik Co-authored-by: Matt Gibson --- .github/whitelist-capital-letters.txt | 8 - apps/browser/src/_locales/en/messages.json | 62 +- .../auth-request-crypto-service.factory.ts | 29 + .../service-factories/auth-service.factory.ts | 16 +- .../device-trust-crypto-service.factory.ts | 74 ++ .../user-verification-api-service.factory.ts | 29 + .../user-verification-service.factory.ts | 51 + .../src/auth/popup/lock.component.html | 14 +- apps/browser/src/auth/popup/lock.component.ts | 21 +- .../login-decryption-options.component.html | 108 ++ .../login-decryption-options.component.ts | 18 + .../popup/login-with-device.component.html | 63 +- .../auth/popup/login-with-device.component.ts | 16 +- .../browser/src/auth/popup/login.component.ts | 2 +- apps/browser/src/auth/popup/services/index.ts | 1 - .../auth/popup/services/lock-guard.service.ts | 8 - .../popup/services/unauth-guard.service.ts | 2 +- apps/browser/src/auth/popup/sso.component.ts | 24 +- .../src/auth/popup/two-factor.component.ts | 33 +- .../autofill-service.factory.ts | 10 +- .../cipher-context-menu-handler.spec.ts | 77 +- .../browser/cipher-context-menu-handler.ts | 14 +- .../src/autofill/services/autofill.service.ts | 9 +- .../src/background/commands.background.ts | 2 +- .../browser/src/background/idle.background.ts | 2 +- .../browser/src/background/main.background.ts | 73 +- .../background/nativeMessaging.background.ts | 54 +- .../devices-api-service.factory.ts | 28 + .../vault-timeout-service.factory.ts | 10 +- .../vault-timeout-settings-service.factory.ts | 14 +- .../services/browser-crypto.service.ts | 27 +- .../services/browser-state.service.spec.ts | 3 +- .../services/browser-state.service.ts | 60 +- apps/browser/src/popup/app-routing.module.ts | 34 +- apps/browser/src/popup/app.module.ts | 2 + .../user-verification.component.html | 4 +- apps/browser/src/popup/scss/base.scss | 34 + .../src/popup/services/services.module.ts | 28 +- .../popup/settings/settings.component.html | 22 +- .../src/popup/settings/settings.component.ts | 191 ++- .../safari/SafariWebExtensionHandler.swift | 34 +- .../vault-timeout.service.ts} | 2 +- apps/cli/src/auth/commands/lock.command.ts | 2 +- apps/cli/src/auth/commands/login.command.ts | 33 +- apps/cli/src/auth/commands/unlock.command.ts | 22 +- apps/cli/src/bw.ts | 52 +- apps/cli/src/commands/serve.command.ts | 7 +- apps/cli/src/program.ts | 6 +- apps/cli/src/vault/create.command.ts | 4 +- .../src/app/accounts/settings.component.html | 106 +- .../src/app/accounts/settings.component.ts | 131 +- apps/desktop/src/app/app-routing.module.ts | 32 +- apps/desktop/src/app/app.component.ts | 23 +- .../user-verification.component.html | 4 +- apps/desktop/src/app/services/init.service.ts | 7 +- apps/desktop/src/auth/lock.component.html | 14 +- apps/desktop/src/auth/lock.component.ts | 21 +- .../login-decryption-options.component.html | 66 + .../login-decryption-options.component.ts | 19 + .../login/login-with-device.component.html | 86 +- .../auth/login/login-with-device.component.ts | 16 +- .../desktop/src/auth/login/login.component.ts | 2 +- apps/desktop/src/auth/login/login.module.ts | 8 +- apps/desktop/src/auth/sso.component.ts | 16 +- apps/desktop/src/auth/two-factor.component.ts | 22 +- apps/desktop/src/locales/en/messages.json | 59 +- apps/desktop/src/main/menu/menu.account.ts | 22 +- apps/desktop/src/main/menu/menu.bitwarden.ts | 5 +- apps/desktop/src/main/menu/menu.file.ts | 5 +- apps/desktop/src/main/menu/menu.first.ts | 53 +- apps/desktop/src/main/menu/menu.updater.ts | 3 +- apps/desktop/src/main/menu/menubar.ts | 19 +- .../services/electron-crypto.service.spec.ts | 91 ++ .../services/electron-crypto.service.ts | 93 +- .../services/electron-state.service.ts | 4 + apps/desktop/src/scss/pages.scss | 20 +- .../native-message-handler.service.ts | 6 +- .../src/services/native-messaging.service.ts | 15 +- .../components/bulk/bulk-confirm.component.ts | 3 +- .../components/reset-password.component.ts | 29 +- .../organization-routing.module.ts | 2 +- .../enroll-master-password-reset.component.ts | 4 +- apps/web/src/app/app.component.ts | 2 +- .../app/auth/accept-organization.component.ts | 15 +- apps/web/src/app/auth/lock.component.ts | 17 +- .../login-decryption-options.component.html | 105 ++ .../login-decryption-options.component.ts | 21 + .../login/login-with-device.component.html | 91 +- .../auth/login/login-with-device.component.ts | 10 +- .../web/src/app/auth/login/login.component.ts | 2 +- apps/web/src/app/auth/login/login.module.ts | 5 +- .../app/auth/recover-two-factor.component.ts | 2 +- .../settings/change-password.component.html | 10 +- .../settings/change-password.component.ts | 75 +- .../emergency-access-takeover.component.ts | 17 +- .../emergency-access-view.component.ts | 9 +- .../emergency-access.component.ts | 9 +- .../settings/two-factor-verify.component.html | 1 - .../user-verification.component.html | 4 +- apps/web/src/app/auth/sso.component.ts | 7 +- apps/web/src/app/auth/two-factor.component.ts | 19 +- .../settings/organization-plans.component.ts | 15 +- apps/web/src/app/core/event.service.ts | 17 + apps/web/src/app/core/init.service.ts | 7 +- apps/web/src/app/guards/home.guard.ts | 24 - .../web/src/app/layouts/navbar.component.html | 2 +- apps/web/src/app/layouts/navbar.component.ts | 9 +- apps/web/src/app/oss-routing.module.ts | 31 +- .../src/app/reports/reports-routing.module.ts | 2 +- .../web/src/app/settings/account.component.ts | 10 +- .../app/settings/change-email.component.ts | 24 +- .../change-kdf-confirmation.component.ts | 18 +- .../app/settings/preferences.component.html | 68 +- .../src/app/settings/preferences.component.ts | 13 +- .../app/settings/security-keys.component.ts | 6 +- .../app/settings/security-routing.module.ts | 2 +- .../src/app/settings/security.component.ts | 6 +- .../app/settings/update-key.component.html | 4 +- .../src/app/settings/update-key.component.ts | 21 +- .../vault-timeout-input.component.html | 4 +- .../src/app/shared/loose-components.module.ts | 2 +- .../components/link-sso.component.ts | 7 +- .../vault/individual-vault/vault.component.ts | 10 +- apps/web/src/locales/en/messages.json | 105 +- .../admin-auth-request-update.request.ts | 2 +- .../device-approvals.component.ts | 18 +- .../organizations-routing.module.ts | 2 +- .../manage/bulk/bulk-confirm.component.ts | 9 +- .../providers/providers-routing.module.ts | 2 +- .../providers/setup/setup.component.ts | 5 +- .../src/app/auth/sso/sso.component.html | 14 +- .../bit-web/src/app/auth/sso/sso.component.ts | 2 +- .../shared/header.component.html | 2 +- .../shared/header.component.ts | 7 + .../secrets-manager/shared/header.stories.ts | 9 + .../app/secrets-manager/sm-routing.module.ts | 2 +- .../src/app/secrets-manager/sm.guard.ts | 2 +- ...base-login-decryption-options.component.ts | 291 ++++ .../components/change-password.component.ts | 29 +- .../src/auth/components/lock.component.ts | 189 ++- .../components/login-with-device.component.ts | 433 ++++-- .../src/auth/components/login.component.ts | 2 +- .../src/auth/components/sso.component.spec.ts | 567 ++++++++ .../src/auth/components/sso.component.ts | 237 +++- .../components/two-factor.component.spec.ts | 451 +++++++ .../auth/components/two-factor.component.ts | 170 ++- .../components/update-password.component.ts | 16 +- .../update-temp-password.component.ts | 44 +- .../components/user-verification.component.ts | 16 +- libs/angular/src/auth/guards/index.ts | 5 + libs/angular/src/auth/guards/lock.guard.ts | 62 +- .../angular/src/auth/guards/redirect.guard.ts | 58 + .../guards/tde-decryption-required.guard.ts | 34 + .../src/components/register.component.ts | 12 +- .../src/components/set-password.component.ts | 42 +- .../src/components/set-pin.component.ts | 30 +- .../settings/vault-timeout-input.component.ts | 11 +- libs/angular/src/services/injection-tokens.ts | 8 +- .../src/services/jslib-services.module.ts | 92 +- .../vault/components/attachments.component.ts | 2 +- .../password-reprompt.service.spec.ts | 34 + .../services/password-reprompt.service.ts | 6 +- libs/common/src/abstractions/api.service.ts | 3 +- .../device-crypto.service.abstraction.ts | 8 - .../devices-api.service.abstraction.ts | 14 - .../devices/devices.service.abstraction.ts | 15 + .../devices/responses/device.response.ts | 13 +- .../abstractions/devices/views/device.view.ts | 17 + .../vault-timeout-settings.service.ts | 56 + .../vault-timeout.service.ts} | 0 .../vaultTimeoutSettings.service.ts | 13 - ...auth-request-crypto.service.abstraction.ts | 24 + .../src/auth/abstractions/auth.service.ts | 4 +- ...device-trust-crypto.service.abstraction.ts | 25 + .../devices-api.service.abstraction.ts | 27 + .../abstractions/key-connector.service.ts | 2 +- ...rd-reset-enrollment.service.abstraction.ts | 26 + .../user-verification.service.abstraction.ts | 12 + .../src/auth/enums/auth-request-type.ts | 1 + .../login-strategies/login.strategy.spec.ts | 91 +- .../auth/login-strategies/login.strategy.ts | 73 +- .../password-login.strategy.spec.ts | 40 +- .../password-login.strategy.ts | 47 +- .../passwordless-login.strategy.spec.ts | 138 ++ .../passwordless-login.strategy.ts | 68 +- .../sso-login.strategy.spec.ts | 219 ++- .../login-strategies/sso-login.strategy.ts | 174 ++- .../user-api-login.strategy.spec.ts | 39 +- .../user-api-login.strategy.ts | 34 +- .../models/domain/admin-auth-req-storable.ts | 41 + .../src/auth/models/domain/auth-result.ts | 7 + .../auth/models/domain/log-in-credentials.ts | 7 +- .../key-connector-user-decryption-option.ts | 3 + .../trusted-device-user-decryption-option.ts | 7 + .../request/update-devices-trust.request.ts | 15 + .../response/identity-token.response.ts | 9 + .../response/protected-device.response.ts | 39 + ...nnector-user-decryption-option.response.ts | 14 + ...-device-user-decryption-option.response.ts | 35 + .../user-decryption-options.response.ts | 39 + ...h-request-crypto.service.implementation.ts | 81 ++ .../auth-request-crypto.service.spec.ts | 165 +++ libs/common/src/auth/services/auth.service.ts | 85 +- ...ice-trust-crypto.service.implementation.ts | 215 +++ .../device-trust-crypto.service.spec.ts | 598 +++++++++ .../devices-api.service.implementation.ts | 96 ++ .../auth/services/key-connector.service.ts | 30 +- ...-enrollment.service.implementation.spec.ts | 123 ++ ...reset-enrollment.service.implementation.ts | 56 + .../user-verification.service.ts | 49 +- libs/common/src/enums/device-type.enum.ts | 13 + libs/common/src/enums/event-type.enum.ts | 3 + .../src/enums/key-suffix-options.enum.ts | 1 + .../platform/abstractions/crypto.service.ts | 454 ++++++- .../platform/abstractions/state.service.ts | 155 ++- .../models/domain/account-keys.spec.ts | 39 +- .../src/platform/models/domain/account.ts | 150 ++- .../platform/models/domain/enc-string.spec.ts | 16 +- .../src/platform/models/domain/enc-string.ts | 16 +- .../models/domain/symmetric-crypto-key.ts | 5 + .../services/config/config.service.ts | 23 +- .../platform/services/crypto.service.spec.ts | 178 +++ .../src/platform/services/crypto.service.ts | 1170 ++++++++++------- .../src/platform/services/state.service.ts | 398 +++++- .../src/platform/services/system.service.ts | 4 +- libs/common/src/services/api.service.ts | 9 +- .../device-crypto.service.implementation.ts | 85 -- .../services/device-crypto.service.spec.ts | 317 ----- .../devices-api.service.implementation.ts | 64 - .../devices/devices.service.implementation.ts | 68 + .../vault-timeout-settings.service.spec.ts | 146 ++ .../vault-timeout-settings.service.ts} | 101 +- .../vault-timeout.service.ts} | 42 +- .../password/password-generation.service.ts | 4 +- .../src/tools/send/services/send.service.ts | 6 +- .../vault/models/domain/attachment.spec.ts | 22 +- .../src/vault/models/domain/attachment.ts | 2 +- .../src/vault/models/domain/card.spec.ts | 14 +- .../src/vault/models/domain/field.spec.ts | 6 +- .../src/vault/models/domain/folder.spec.ts | 4 +- .../src/vault/models/domain/identity.spec.ts | 38 +- .../src/vault/models/domain/login.spec.ts | 8 +- .../src/vault/models/domain/password.spec.ts | 4 +- .../src/vault/services/cipher.service.spec.ts | 8 +- .../src/vault/services/cipher.service.ts | 37 +- .../src/vault/services/collection.service.ts | 2 +- .../vault/services/folder/folder.service.ts | 2 +- .../src/vault/services/sync/sync.service.ts | 4 +- .../services/vault-export.service.spec.ts | 8 +- 249 files changed, 10155 insertions(+), 2558 deletions(-) create mode 100644 apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts create mode 100644 apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts create mode 100644 apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts create mode 100644 apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts create mode 100644 apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html create mode 100644 apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts delete mode 100644 apps/browser/src/auth/popup/services/lock-guard.service.ts create mode 100644 apps/browser/src/background/service-factories/devices-api-service.factory.ts rename apps/browser/src/services/{vaultTimeout/vaultTimeout.service.ts => vault-timeout/vault-timeout.service.ts} (93%) create mode 100644 apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.html create mode 100644 apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts create mode 100644 apps/desktop/src/platform/services/electron-crypto.service.spec.ts create mode 100644 apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html create mode 100644 apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts rename apps/web/src/app/{ => auth}/settings/change-password.component.html (94%) rename apps/web/src/app/{ => auth}/settings/change-password.component.ts (82%) delete mode 100644 apps/web/src/app/guards/home.guard.ts create mode 100644 libs/angular/src/auth/components/base-login-decryption-options.component.ts create mode 100644 libs/angular/src/auth/components/sso.component.spec.ts create mode 100644 libs/angular/src/auth/components/two-factor.component.spec.ts create mode 100644 libs/angular/src/auth/guards/index.ts create mode 100644 libs/angular/src/auth/guards/redirect.guard.ts create mode 100644 libs/angular/src/auth/guards/tde-decryption-required.guard.ts create mode 100644 libs/angular/src/vault/services/password-reprompt.service.spec.ts delete mode 100644 libs/common/src/abstractions/device-crypto.service.abstraction.ts delete mode 100644 libs/common/src/abstractions/devices/devices-api.service.abstraction.ts create mode 100644 libs/common/src/abstractions/devices/devices.service.abstraction.ts create mode 100644 libs/common/src/abstractions/devices/views/device.view.ts create mode 100644 libs/common/src/abstractions/vault-timeout/vault-timeout-settings.service.ts rename libs/common/src/abstractions/{vaultTimeout/vaultTimeout.service.ts => vault-timeout/vault-timeout.service.ts} (100%) delete mode 100644 libs/common/src/abstractions/vaultTimeout/vaultTimeoutSettings.service.ts create mode 100644 libs/common/src/auth/abstractions/auth-request-crypto.service.abstraction.ts create mode 100644 libs/common/src/auth/abstractions/device-trust-crypto.service.abstraction.ts create mode 100644 libs/common/src/auth/abstractions/devices-api.service.abstraction.ts create mode 100644 libs/common/src/auth/abstractions/password-reset-enrollment.service.abstraction.ts create mode 100644 libs/common/src/auth/login-strategies/passwordless-login.strategy.spec.ts create mode 100644 libs/common/src/auth/models/domain/admin-auth-req-storable.ts create mode 100644 libs/common/src/auth/models/domain/user-decryption-options/key-connector-user-decryption-option.ts create mode 100644 libs/common/src/auth/models/domain/user-decryption-options/trusted-device-user-decryption-option.ts create mode 100644 libs/common/src/auth/models/request/update-devices-trust.request.ts create mode 100644 libs/common/src/auth/models/response/protected-device.response.ts create mode 100644 libs/common/src/auth/models/response/user-decryption-options/key-connector-user-decryption-option.response.ts create mode 100644 libs/common/src/auth/models/response/user-decryption-options/trusted-device-user-decryption-option.response.ts create mode 100644 libs/common/src/auth/models/response/user-decryption-options/user-decryption-options.response.ts create mode 100644 libs/common/src/auth/services/auth-request-crypto.service.implementation.ts create mode 100644 libs/common/src/auth/services/auth-request-crypto.service.spec.ts create mode 100644 libs/common/src/auth/services/device-trust-crypto.service.implementation.ts create mode 100644 libs/common/src/auth/services/device-trust-crypto.service.spec.ts create mode 100644 libs/common/src/auth/services/devices-api.service.implementation.ts create mode 100644 libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts create mode 100644 libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts delete mode 100644 libs/common/src/services/device-crypto.service.implementation.ts delete mode 100644 libs/common/src/services/device-crypto.service.spec.ts delete mode 100644 libs/common/src/services/devices/devices-api.service.implementation.ts create mode 100644 libs/common/src/services/devices/devices.service.implementation.ts create mode 100644 libs/common/src/services/vault-timeout/vault-timeout-settings.service.spec.ts rename libs/common/src/services/{vaultTimeout/vaultTimeoutSettings.service.ts => vault-timeout/vault-timeout-settings.service.ts} (50%) rename libs/common/src/services/{vaultTimeout/vaultTimeout.service.ts => vault-timeout/vault-timeout.service.ts} (79%) diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 17047f4333d..a2ed46ce99b 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -2,10 +2,7 @@ ./apps/browser/src/safari/desktop/Assets.xcassets/AccentColor.colorset ./apps/browser/src/safari/desktop/Assets.xcassets/AppIcon.appiconset ./apps/browser/src/safari/desktop/Base.lproj -./apps/browser/src/services/vaultTimeout ./apps/browser/store/windows/Assets -./libs/common/src/abstractions/vaultTimeout -./libs/common/src/services/vaultTimeout ./bitwarden_license/README.md ./libs/angular/src/directives/cipherListVirtualScroll.directive.ts ./libs/angular/src/scss/webfonts/Open_Sans-italic-700.woff @@ -25,11 +22,7 @@ ./libs/common/src/misc/linkedFieldOption.decorator.ts ./libs/common/src/misc/serviceUtils.ts ./libs/common/src/misc/serviceUtils.spec.ts -./libs/common/src/abstractions/vaultTimeout/vaultTimeoutSettings.service.ts -./libs/common/src/abstractions/vaultTimeout/vaultTimeout.service.ts ./libs/common/src/abstractions/anonymousHub.service.ts -./libs/common/src/services/vaultTimeout/vaultTimeoutSettings.service.ts -./libs/common/src/services/vaultTimeout/vaultTimeout.service.ts ./libs/common/src/services/anonymousHub.service.ts ./libs/auth/README.md ./README.md @@ -78,5 +71,4 @@ ./apps/browser/src/safari/safari/SafariWebExtensionHandler.swift ./apps/browser/src/safari/safari/Info.plist ./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist -./apps/browser/src/services/vaultTimeout/vaultTimeout.service.ts ./SECURITY.md diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 400e75dd1b4..6de09d355aa 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -338,6 +338,9 @@ "other": { "message": "Other" }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, "rateExtension": { "message": "Rate the extension" }, @@ -1602,6 +1605,12 @@ "biometricsNotSupportedDesc": { "message": "Browser biometrics is not supported on this device." }, + "biometricsFailedTitle": { + "message": "Biometrics failed" + }, + "biometricsFailedDesc": { + "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + }, "nativeMessaginPermissionErrorTitle": { "message": "Permission not provided" }, @@ -2143,8 +2152,8 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, - "logInInitiated": { - "message": "Log in initiated" + "loginInitiated": { + "message": "Login initiated" }, "exposedMasterPassword": { "message": "Exposed Master Password" @@ -2230,6 +2239,31 @@ "opensInANewWindow": { "message": "Opens in a new window" }, + "deviceApprovalRequired": { + "message": "Device approval required. Select an approval option below:" + }, + "rememberThisDevice": { + "message": "Remember this device" + }, + "uncheckIfPublicDevice": { + "message": "Uncheck if using a public device" + }, + "approveFromYourOtherDevice": { + "message": "Approve from your other device" + }, + "requestAdminApproval": { + "message": "Request admin approval" + }, + "approveWithMasterPassword": { + "message": "Approve with master password" + }, + "ssoIdentifierRequired": { + "message": "Organization SSO identifier is required." + }, + "eu": { + "message": "EU", + "description": "European Union" + }, "usDomain": { "message": "bitwarden.com" }, @@ -2244,5 +2278,29 @@ }, "display": { "message": "Display" + }, + "accountSuccessfullyCreated": { + "message": "Account successfully created!" + }, + "adminApprovalRequested": { + "message": "Admin approval requested" + }, + "adminApprovalRequestSentToAdmins": { + "message": "Your request has been sent to your admin." + }, + "youWillBeNotifiedOnceApproved": { + "message": "You will be notified once approved." + }, + "troubleLoggingIn": { + "message": "Trouble logging in?" + }, + "loginApproved": { + "message": "Login approved" + }, + "userEmailMissing": { + "message": "User email missing" + }, + "deviceTrusted": { + "message": "Device trusted" } } diff --git a/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts new file mode 100644 index 00000000000..e1757f98129 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts @@ -0,0 +1,29 @@ +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; +import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; + +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + CachedServices, + FactoryOptions, + factory, +} from "../../../platform/background/service-factories/factory-options"; + +type AuthRequestCryptoServiceFactoryOptions = FactoryOptions; + +export type AuthRequestCryptoServiceInitOptions = AuthRequestCryptoServiceFactoryOptions & + CryptoServiceInitOptions; + +export function authRequestCryptoServiceFactory( + cache: { authRequestCryptoService?: AuthRequestCryptoServiceAbstraction } & CachedServices, + opts: AuthRequestCryptoServiceInitOptions +): Promise { + return factory( + cache, + "authRequestCryptoService", + opts, + async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts)) + ); +} diff --git a/apps/browser/src/auth/background/service-factories/auth-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-service.factory.ts index 5612cedb91c..6aaeb476369 100644 --- a/apps/browser/src/auth/background/service-factories/auth-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/auth-service.factory.ts @@ -52,6 +52,14 @@ import { PasswordStrengthServiceInitOptions, } from "../../../tools/background/service_factories/password-strength-service.factory"; +import { + authRequestCryptoServiceFactory, + AuthRequestCryptoServiceInitOptions, +} from "./auth-request-crypto-service.factory"; +import { + deviceTrustCryptoServiceFactory, + DeviceTrustCryptoServiceInitOptions, +} from "./device-trust-crypto-service.factory"; import { keyConnectorServiceFactory, KeyConnectorServiceInitOptions, @@ -75,7 +83,9 @@ export type AuthServiceInitOptions = AuthServiceFactoyOptions & I18nServiceInitOptions & EncryptServiceInitOptions & PolicyServiceInitOptions & - PasswordStrengthServiceInitOptions; + PasswordStrengthServiceInitOptions & + DeviceTrustCryptoServiceInitOptions & + AuthRequestCryptoServiceInitOptions; export function authServiceFactory( cache: { authService?: AbstractAuthService } & CachedServices, @@ -101,7 +111,9 @@ export function authServiceFactory( await i18nServiceFactory(cache, opts), await encryptServiceFactory(cache, opts), await passwordStrengthServiceFactory(cache, opts), - await policyServiceFactory(cache, opts) + await policyServiceFactory(cache, opts), + await deviceTrustCryptoServiceFactory(cache, opts), + await authRequestCryptoServiceFactory(cache, opts) ) ); } diff --git a/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts new file mode 100644 index 00000000000..430d50fea75 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts @@ -0,0 +1,74 @@ +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; +import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; + +import { + DevicesApiServiceInitOptions, + devicesApiServiceFactory, +} from "../../../background/service-factories/devices-api-service.factory"; +import { + AppIdServiceInitOptions, + appIdServiceFactory, +} from "../../../platform/background/service-factories/app-id-service.factory"; +import { + CryptoFunctionServiceInitOptions, + cryptoFunctionServiceFactory, +} from "../../../platform/background/service-factories/crypto-function-service.factory"; +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + EncryptServiceInitOptions, + encryptServiceFactory, +} from "../../../platform/background/service-factories/encrypt-service.factory"; +import { + CachedServices, + FactoryOptions, + factory, +} from "../../../platform/background/service-factories/factory-options"; +import { + I18nServiceInitOptions, + i18nServiceFactory, +} from "../../../platform/background/service-factories/i18n-service.factory"; +import { + PlatformUtilsServiceInitOptions, + platformUtilsServiceFactory, +} from "../../../platform/background/service-factories/platform-utils-service.factory"; +import { + StateServiceInitOptions, + stateServiceFactory, +} from "../../../platform/background/service-factories/state-service.factory"; + +type DeviceTrustCryptoServiceFactoryOptions = FactoryOptions; + +export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactoryOptions & + CryptoFunctionServiceInitOptions & + CryptoServiceInitOptions & + EncryptServiceInitOptions & + StateServiceInitOptions & + AppIdServiceInitOptions & + DevicesApiServiceInitOptions & + I18nServiceInitOptions & + PlatformUtilsServiceInitOptions; + +export function deviceTrustCryptoServiceFactory( + cache: { deviceTrustCryptoService?: DeviceTrustCryptoServiceAbstraction } & CachedServices, + opts: DeviceTrustCryptoServiceInitOptions +): Promise { + return factory( + cache, + "deviceTrustCryptoService", + opts, + async () => + new DeviceTrustCryptoService( + await cryptoFunctionServiceFactory(cache, opts), + await cryptoServiceFactory(cache, opts), + await encryptServiceFactory(cache, opts), + await stateServiceFactory(cache, opts), + await appIdServiceFactory(cache, opts), + await devicesApiServiceFactory(cache, opts), + await i18nServiceFactory(cache, opts), + await platformUtilsServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts new file mode 100644 index 00000000000..01bfb0f13cb --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts @@ -0,0 +1,29 @@ +import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction"; +import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; + +import { + ApiServiceInitOptions, + apiServiceFactory, +} from "../../../platform/background/service-factories/api-service.factory"; +import { + FactoryOptions, + CachedServices, + factory, +} from "../../../platform/background/service-factories/factory-options"; + +type UserVerificationApiServiceFactoryOptions = FactoryOptions; + +export type UserVerificationApiServiceInitOptions = UserVerificationApiServiceFactoryOptions & + ApiServiceInitOptions; + +export function userVerificationApiServiceFactory( + cache: { userVerificationApiService?: UserVerificationApiServiceAbstraction } & CachedServices, + opts: UserVerificationApiServiceInitOptions +): Promise { + return factory( + cache, + "userVerificationApiService", + opts, + async () => new UserVerificationApiService(await apiServiceFactory(cache, opts)) + ); +} diff --git a/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts new file mode 100644 index 00000000000..79d327c9485 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts @@ -0,0 +1,51 @@ +import { UserVerificationService as AbstractUserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; + +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + FactoryOptions, + CachedServices, + factory, +} from "../../../platform/background/service-factories/factory-options"; +import { + I18nServiceInitOptions, + i18nServiceFactory, +} from "../../../platform/background/service-factories/i18n-service.factory"; +import { + StateServiceInitOptions, + stateServiceFactory, +} from "../../../platform/background/service-factories/state-service.factory"; + +import { + UserVerificationApiServiceInitOptions, + userVerificationApiServiceFactory, +} from "./user-verification-api-service.factory"; + +type UserVerificationServiceFactoryOptions = FactoryOptions; + +export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions & + StateServiceInitOptions & + CryptoServiceInitOptions & + I18nServiceInitOptions & + UserVerificationApiServiceInitOptions; + +export function userVerificationServiceFactory( + cache: { userVerificationService?: AbstractUserVerificationService } & CachedServices, + opts: UserVerificationServiceInitOptions +): Promise { + return factory( + cache, + "userVerificationService", + opts, + async () => + new UserVerificationService( + await stateServiceFactory(cache, opts), + await cryptoServiceFactory(cache, opts), + await i18nServiceFactory(cache, opts), + await userVerificationApiServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/auth/popup/lock.component.html b/apps/browser/src/auth/popup/lock.component.html index cf964c15646..e787e0106d1 100644 --- a/apps/browser/src/auth/popup/lock.component.html +++ b/apps/browser/src/auth/popup/lock.component.html @@ -5,14 +5,20 @@ {{ "verifyIdentity" | i18n }}
- +
-
-
+
+
-
+
{ - document.getElementById(this.pinLock ? "pin" : "masterPassword").focus(); + document.getElementById(this.pinEnabled ? "pin" : "masterPassword")?.focus(); if ( this.biometricLock && !disableAutoBiometricsPrompt && @@ -93,7 +96,7 @@ export class LockComponent extends BaseLockComponent { }, 100); } - async unlockBiometric(): Promise { + override async unlockBiometric(): Promise { if (!this.biometricLock) { return; } diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html new file mode 100644 index 00000000000..32e3ea0c598 --- /dev/null +++ b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html @@ -0,0 +1,108 @@ +
+
+

+ {{ "loginInitiated" | i18n }} +

+
+ + +
diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..7fac9a42b06 --- /dev/null +++ b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,18 @@ +import { Component } from "@angular/core"; + +import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; + +@Component({ + selector: "browser-login-decryption-options", + templateUrl: "login-decryption-options.component.html", +}) +export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { + override async createUser(): Promise { + try { + await super.createUser(); + await this.router.navigate(["/tabs/vault"]); + } catch (error) { + this.validationService.showError(error); + } + } +} diff --git a/apps/browser/src/auth/popup/login-with-device.component.html b/apps/browser/src/auth/popup/login-with-device.component.html index d794b7d212b..127f7ec96fe 100644 --- a/apps/browser/src/auth/popup/login-with-device.component.html +++ b/apps/browser/src/auth/popup/login-with-device.component.html @@ -5,32 +5,57 @@
diff --git a/apps/browser/src/auth/popup/login-with-device.component.ts b/apps/browser/src/auth/popup/login-with-device.component.ts index cf0e57b5ee5..f3a1dfffaa0 100644 --- a/apps/browser/src/auth/popup/login-with-device.component.ts +++ b/apps/browser/src/auth/popup/login-with-device.component.ts @@ -1,10 +1,13 @@ +import { Location } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -42,7 +45,10 @@ export class LoginWithDeviceComponent validationService: ValidationService, stateService: StateService, loginService: LoginService, - syncService: SyncService + syncService: SyncService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + authReqCryptoService: AuthRequestCryptoServiceAbstraction, + private location: Location ) { super( router, @@ -59,10 +65,16 @@ export class LoginWithDeviceComponent anonymousHubService, validationService, stateService, - loginService + loginService, + deviceTrustCryptoService, + authReqCryptoService ); super.onSuccessfulLogin = async () => { await syncService.fullSync(true); }; } + + protected back() { + this.location.back(); + } } diff --git a/apps/browser/src/auth/popup/login.component.ts b/apps/browser/src/auth/popup/login.component.ts index 776b792fa1d..d9b789e4d81 100644 --- a/apps/browser/src/auth/popup/login.component.ts +++ b/apps/browser/src/auth/popup/login.component.ts @@ -4,8 +4,8 @@ import { ActivatedRoute, Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; diff --git a/apps/browser/src/auth/popup/services/index.ts b/apps/browser/src/auth/popup/services/index.ts index 06bfe0009bd..63563f61fd9 100644 --- a/apps/browser/src/auth/popup/services/index.ts +++ b/apps/browser/src/auth/popup/services/index.ts @@ -1,2 +1 @@ -export { LockGuardService } from "./lock-guard.service"; export { UnauthGuardService } from "./unauth-guard.service"; diff --git a/apps/browser/src/auth/popup/services/lock-guard.service.ts b/apps/browser/src/auth/popup/services/lock-guard.service.ts deleted file mode 100644 index ef6ebc73aca..00000000000 --- a/apps/browser/src/auth/popup/services/lock-guard.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { LockGuard as BaseLockGuardService } from "@bitwarden/angular/auth/guards/lock.guard"; - -@Injectable() -export class LockGuardService extends BaseLockGuardService { - protected homepage = "tabs/current"; -} diff --git a/apps/browser/src/auth/popup/services/unauth-guard.service.ts b/apps/browser/src/auth/popup/services/unauth-guard.service.ts index 4aa700b5568..062239a7d36 100644 --- a/apps/browser/src/auth/popup/services/unauth-guard.service.ts +++ b/apps/browser/src/auth/popup/services/unauth-guard.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; -import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards/unauth.guard"; +import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards"; @Injectable() export class UnauthGuardService extends BaseUnauthGuardService { diff --git a/apps/browser/src/auth/popup/sso.component.ts b/apps/browser/src/auth/popup/sso.component.ts index 2214e91687a..9d04701680e 100644 --- a/apps/browser/src/auth/popup/sso.component.ts +++ b/apps/browser/src/auth/popup/sso.component.ts @@ -1,11 +1,12 @@ -import { Component } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { SsoComponent as BaseSsoComponent } from "@bitwarden/angular/auth/components/sso.component"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -35,7 +36,8 @@ export class SsoComponent extends BaseSsoComponent { syncService: SyncService, environmentService: EnvironmentService, logService: LogService, - private vaultTimeoutService: VaultTimeoutService + configService: ConfigServiceAbstraction, + @Inject(WINDOW) private win: Window ) { super( authService, @@ -48,7 +50,8 @@ export class SsoComponent extends BaseSsoComponent { cryptoFunctionService, environmentService, passwordGenerationService, - logService + logService, + configService ); const url = this.environmentService.getWebVaultUrl(); @@ -57,15 +60,22 @@ export class SsoComponent extends BaseSsoComponent { this.clientId = "browser"; super.onSuccessfulLogin = async () => { - await syncService.fullSync(true); + syncService.fullSync(true); // If the vault is unlocked then this will clear keys from memory, which we don't want to do if ((await this.authService.getAuthStatus()) !== AuthenticationStatus.Unlocked) { BrowserApi.reloadOpenWindows(); } - const thisWindow = window.open("", "_self"); - thisWindow.close(); + this.win.close(); + }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); + }; + + super.onSuccessfulLoginTdeNavigate = async () => { + this.win.close(); }; } } diff --git a/apps/browser/src/auth/popup/two-factor.component.ts b/apps/browser/src/auth/popup/two-factor.component.ts index 03821bb22f1..c0af31d25e5 100644 --- a/apps/browser/src/auth/popup/two-factor.component.ts +++ b/apps/browser/src/auth/popup/two-factor.component.ts @@ -1,8 +1,9 @@ -import { Component } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; @@ -10,6 +11,7 @@ import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -48,7 +50,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { twoFactorService: TwoFactorService, appIdService: AppIdService, loginService: LoginService, - private dialogService: DialogService + configService: ConfigServiceAbstraction, + private dialogService: DialogService, + @Inject(WINDOW) protected win: Window ) { super( authService, @@ -56,19 +60,28 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { i18nService, apiService, platformUtilsService, - window, + win, environmentService, stateService, route, logService, twoFactorService, appIdService, - loginService + loginService, + configService ); - super.onSuccessfulLogin = () => { - this.loginService.clearValues(); - return syncService.fullSync(true); + super.onSuccessfulLogin = async () => { + syncService.fullSync(true); }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); + }; + + super.onSuccessfulLoginTdeNavigate = async () => { + this.win.close(); + }; + super.successRoute = "/tabs/vault"; // FIXME: Chromium 110 has broken WebAuthn support in extensions via an iframe this.webAuthnNewTab = true; @@ -117,11 +130,11 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.sso === "true") { - super.onSuccessfulLogin = () => { + super.onSuccessfulLogin = async () => { // This is not awaited so we don't pause the application while the sync is happening. // This call is executed by the service that lives in the background script so it will continue // the sync even if this tab closes. - const syncPromise = this.syncService.fullSync(true); + this.syncService.fullSync(true); // Force sidebars (FF && Opera) to reload while exempting current window // because we are just going to close the current window. @@ -130,8 +143,6 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { // We don't need this window anymore because the intent is for the user to be left // on the web vault screen which tells them to continue in the browser extension (sidebar or popup) BrowserApi.closeBitwardenExtensionTab(); - - return syncPromise; }; } }); diff --git a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts index a802fd8cf1c..efa5bdcffbb 100644 --- a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts +++ b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts @@ -2,6 +2,10 @@ import { TotpServiceInitOptions, totpServiceFactory, } from "../../../auth/background/service-factories/totp-service.factory"; +import { + UserVerificationServiceInitOptions, + userVerificationServiceFactory, +} from "../../../auth/background/service-factories/user-verification-service.factory"; import { EventCollectionServiceInitOptions, eventCollectionServiceFactory, @@ -38,7 +42,8 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions & TotpServiceInitOptions & EventCollectionServiceInitOptions & LogServiceInitOptions & - SettingsServiceInitOptions; + SettingsServiceInitOptions & + UserVerificationServiceInitOptions; export function autofillServiceFactory( cache: { autofillService?: AbstractAutoFillService } & CachedServices, @@ -55,7 +60,8 @@ export function autofillServiceFactory( await totpServiceFactory(cache, opts), await eventCollectionServiceFactory(cache, opts), await logServiceFactory(cache, opts), - await settingsServiceFactory(cache, opts) + await settingsServiceFactory(cache, opts), + await userVerificationServiceFactory(cache, opts) ) ); } diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts index 9db6574aada..dbe391ce4ab 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; @@ -13,6 +14,7 @@ describe("CipherContextMenuHandler", () => { let mainContextMenuHandler: MockProxy; let authService: MockProxy; let cipherService: MockProxy; + let userVerificationService: MockProxy; let sut: CipherContextMenuHandler; @@ -20,10 +22,17 @@ describe("CipherContextMenuHandler", () => { mainContextMenuHandler = mock(); authService = mock(); cipherService = mock(); + userVerificationService = mock(); + userVerificationService.hasMasterPassword.mockResolvedValue(true); jest.spyOn(MainContextMenuHandler, "removeAll").mockResolvedValue(); - sut = new CipherContextMenuHandler(mainContextMenuHandler, authService, cipherService); + sut = new CipherContextMenuHandler( + mainContextMenuHandler, + authService, + cipherService, + userVerificationService + ); }); afterEach(() => jest.resetAllMocks()); @@ -83,11 +92,11 @@ describe("CipherContextMenuHandler", () => { }; cipherService.getAllDecryptedForUrl.mockResolvedValue([ - null, - undefined, - { type: CipherType.Card }, - { type: CipherType.Login, reprompt: CipherRepromptType.Password }, - realCipher, + null, // invalid cipher + undefined, // invalid cipher + { type: CipherType.Card }, // invalid cipher + { type: CipherType.Login, reprompt: CipherRepromptType.Password }, // invalid cipher + realCipher, // valid cipher ] as any[]); await sut.update("https://test.com"); @@ -105,5 +114,61 @@ describe("CipherContextMenuHandler", () => { realCipher ); }); + + it("adds ciphers with master password reprompt if the user does not have a master password", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); + + // User does not have a master password, or has one but hasn't logged in with it (key connector user or TDE user) + userVerificationService.hasMasterPasswordAndMasterKeyHash.mockResolvedValue(false); + + mainContextMenuHandler.init.mockResolvedValue(true); + + const realCipher = { + id: "5", + type: CipherType.Login, + reprompt: CipherRepromptType.None, + name: "Test Cipher", + login: { username: "Test Username" }, + }; + + const repromptCipher = { + id: "6", + type: CipherType.Login, + reprompt: CipherRepromptType.Password, + name: "Test Reprompt Cipher", + login: { username: "Test Username" }, + }; + + cipherService.getAllDecryptedForUrl.mockResolvedValue([ + null, // invalid cipher + undefined, // invalid cipher + { type: CipherType.Card }, // invalid cipher + repromptCipher, // valid cipher + realCipher, // valid cipher + ] as any[]); + + await sut.update("https://test.com"); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledTimes(1); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); + + // Should call this twice, once for each valid cipher + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(2); + + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( + "Test Cipher (Test Username)", + "5", + "https://test.com", + realCipher + ); + + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( + "Test Reprompt Cipher (Test Username)", + "6", + "https://test.com", + repromptCipher + ); + }); }); }); diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index fe6479aae51..1d1be8f8386 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -1,4 +1,5 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -11,6 +12,7 @@ import { authServiceFactory, AuthServiceInitOptions, } from "../../auth/background/service-factories/auth-service.factory"; +import { userVerificationServiceFactory } from "../../auth/background/service-factories/user-verification-service.factory"; import { Account } from "../../models/account"; import { CachedServices } from "../../platform/background/service-factories/factory-options"; import { BrowserApi } from "../../platform/browser/browser-api"; @@ -37,7 +39,8 @@ export class CipherContextMenuHandler { constructor( private mainContextMenuHandler: MainContextMenuHandler, private authService: AuthService, - private cipherService: CipherService + private cipherService: CipherService, + private userVerificationService: UserVerificationService ) {} static async create(cachedServices: CachedServices) { @@ -76,7 +79,8 @@ export class CipherContextMenuHandler { return new CipherContextMenuHandler( await MainContextMenuHandler.mv3Create(cachedServices), await authServiceFactory(cachedServices, serviceOptions), - await cipherServiceFactory(cachedServices, serviceOptions) + await cipherServiceFactory(cachedServices, serviceOptions), + await userVerificationServiceFactory(cachedServices, serviceOptions) ); } @@ -176,7 +180,11 @@ export class CipherContextMenuHandler { } private async updateForCipher(url: string, cipher: CipherView) { - if (cipher == null || cipher.type !== CipherType.Login) { + if ( + cipher == null || + cipher.type !== CipherType.Login || + (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) + ) { return; } diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 8b7ec916534..571b4af7213 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -1,6 +1,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { EventType, FieldType, UriMatchType } from "@bitwarden/common/enums"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -45,7 +46,8 @@ export default class AutofillService implements AutofillServiceInterface { private totpService: TotpService, private eventCollectionService: EventCollectionService, private logService: LogService, - private settingsService: SettingsService + private settingsService: SettingsService, + private userVerificationService: UserVerificationService ) {} getFormsWithPasswordFields(pageDetails: AutofillPageDetails): FormData[] { @@ -238,7 +240,10 @@ export default class AutofillService implements AutofillServiceInterface { return null; } - if (cipher.reprompt !== CipherRepromptType.None) { + if ( + cipher.reprompt !== CipherRepromptType.None && + (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) + ) { await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { cipherId: cipher.id, action: "autofill", diff --git a/apps/browser/src/background/commands.background.ts b/apps/browser/src/background/commands.background.ts index 118953b9da0..0cbf91c6666 100644 --- a/apps/browser/src/background/commands.background.ts +++ b/apps/browser/src/background/commands.background.ts @@ -1,4 +1,4 @@ -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 3854d2383b4..7200301c795 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -1,5 +1,5 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 59a1b444e09..617acc2bf78 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1,27 +1,33 @@ import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; +import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service"; import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service"; -import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction"; import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; +import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; +import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/services/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service"; @@ -59,12 +65,13 @@ import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/we import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service"; import { ApiService } from "@bitwarden/common/services/api.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; +import { DevicesServiceImplementation } from "@bitwarden/common/services/devices/devices.service.implementation"; import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/services/notifications.service"; import { SearchService } from "@bitwarden/common/services/search.service"; import { TotpService } from "@bitwarden/common/services/totp.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; import { PasswordGenerationService, PasswordGenerationServiceAbstraction, @@ -128,7 +135,7 @@ import { KeyGenerationService } from "../platform/services/key-generation.servic import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service"; import { BrowserSendService } from "../services/browser-send.service"; import { BrowserSettingsService } from "../services/browser-settings.service"; -import VaultTimeoutService from "../services/vaultTimeout/vaultTimeout.service"; +import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service"; import { BrowserFolderService } from "../vault/services/browser-folder.service"; import { VaultFilterService } from "../vault/services/vault-filter.service"; @@ -156,7 +163,7 @@ export default class MainBackground { cipherService: CipherServiceAbstraction; folderService: InternalFolderServiceAbstraction; collectionService: CollectionServiceAbstraction; - vaultTimeoutService: VaultTimeoutServiceAbstraction; + vaultTimeoutService: VaultTimeoutService; vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction; syncService: SyncServiceAbstraction; passwordGenerationService: PasswordGenerationServiceAbstraction; @@ -196,6 +203,10 @@ export default class MainBackground { cipherContextMenuHandler: CipherContextMenuHandler; configService: ConfigServiceAbstraction; configApiService: ConfigApiServiceAbstraction; + devicesApiService: DevicesApiServiceAbstraction; + devicesService: DevicesServiceAbstraction; + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; + authRequestCryptoService: AuthRequestCryptoServiceAbstraction; browserPopoutWindowService: BrowserPopoutWindowService; // Passed to the popup for Safari to workaround issues with theming, downloading, etc. @@ -387,6 +398,23 @@ export default class MainBackground { that.runtimeBackground.processMessage(message, that as any, null); }; })(); + + this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); + this.deviceTrustCryptoService = new DeviceTrustCryptoService( + this.cryptoFunctionService, + this.cryptoService, + this.encryptService, + this.stateService, + this.appIdService, + this.devicesApiService, + this.i18nService, + this.platformUtilsService + ); + + this.devicesService = new DevicesServiceImplementation(this.devicesApiService); + + this.authRequestCryptoService = new AuthRequestCryptoServiceImplementation(this.cryptoService); + this.authService = new AuthService( this.cryptoService, this.apiService, @@ -402,14 +430,26 @@ export default class MainBackground { this.i18nService, this.encryptService, this.passwordStrengthService, - this.policyService + this.policyService, + this.deviceTrustCryptoService, + this.authRequestCryptoService + ); + + this.userVerificationApiService = new UserVerificationApiService(this.apiService); + + this.userVerificationService = new UserVerificationService( + this.stateService, + this.cryptoService, + this.i18nService, + this.userVerificationApiService ); this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService( this.cryptoService, this.tokenService, this.policyService, - this.stateService + this.stateService, + this.userVerificationService ); this.vaultTimeoutService = new VaultTimeoutService( @@ -420,7 +460,6 @@ export default class MainBackground { this.platformUtilsService, this.messagingService, this.searchService, - this.keyConnectorService, this.stateService, this.authService, this.vaultTimeoutSettingsService, @@ -471,13 +510,15 @@ export default class MainBackground { this.eventUploadService ); this.totpService = new TotpService(this.cryptoFunctionService, this.logService); + this.autofillService = new AutofillService( this.cipherService, this.stateService, this.totpService, this.eventCollectionService, this.logService, - this.settingsService + this.settingsService, + this.userVerificationService ); this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); this.exportService = new VaultExportService( @@ -499,15 +540,6 @@ export default class MainBackground { this.authService, this.messagingService ); - - this.userVerificationApiService = new UserVerificationApiService(this.apiService); - - this.userVerificationService = new UserVerificationService( - this.cryptoService, - this.i18nService, - this.userVerificationApiService - ); - this.configService = new ConfigService( this.stateService, this.configApiService, @@ -638,7 +670,8 @@ export default class MainBackground { this.cipherContextMenuHandler = new CipherContextMenuHandler( this.mainContextMenuHandler, this.authService, - this.cipherService + this.cipherService, + this.userVerificationService ); } } @@ -648,7 +681,7 @@ export default class MainBackground { await this.stateService.init(); - await (this.vaultTimeoutService as VaultTimeoutService).init(true); + await this.vaultTimeoutService.init(true); await (this.i18nService as BrowserI18nService).init(); await (this.eventUploadService as EventUploadService).init(true); await this.runtimeBackground.init(); diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index e482c7cc83e..d393022b7b9 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -10,7 +10,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.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 { + MasterKey, + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { BrowserApi } from "../platform/browser/browser-api"; @@ -42,6 +46,7 @@ type ReceiveMessage = { // Unlock key keyB64?: string; + userKeyB64?: string; }; type ReceiveMessageOuter = { @@ -320,16 +325,55 @@ export class NativeMessagingBackground { } if (message.response === "unlocked") { - await this.cryptoService.setKey( - new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64)) - ); + try { + if (message.userKeyB64) { + const userKey = new SymmetricCryptoKey( + Utils.fromB64ToArray(message.userKeyB64) + ) as UserKey; + await this.cryptoService.setUserKey(userKey); + } else if (message.keyB64) { + // Backwards compatibility to support cases in which the user hasn't updated their desktop app + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472) + let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey(); + encUserKey ||= await this.stateService.getMasterKeyEncryptedUserKey(); + if (!encUserKey) { + throw new Error("No encrypted user key found"); + } + const masterKey = new SymmetricCryptoKey( + Utils.fromB64ToArray(message.keyB64) + ) as MasterKey; + const userKey = await this.cryptoService.decryptUserKeyWithMasterKey( + masterKey, + new EncString(encUserKey) + ); + await this.cryptoService.setMasterKey(masterKey); + await this.cryptoService.setUserKey(userKey); + } else { + throw new Error("No key received"); + } + } catch (e) { + this.logService.error("Unable to set key: " + e); + this.messagingService.send("showDialog", { + title: { key: "biometricsFailedTitle" }, + content: { key: "biometricsFailedDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "danger", + }); + + // Exit early + if (this.resolver) { + this.resolver(message); + } + return; + } // Verify key is correct by attempting to decrypt a secret try { await this.cryptoService.getFingerprint(await this.stateService.getUserId()); } catch (e) { this.logService.error("Unable to verify key: " + e); - await this.cryptoService.clearKey(); + await this.cryptoService.clearKeys(); this.showWrongUserDialog(); // Exit early diff --git a/apps/browser/src/background/service-factories/devices-api-service.factory.ts b/apps/browser/src/background/service-factories/devices-api-service.factory.ts new file mode 100644 index 00000000000..8999b7c2c72 --- /dev/null +++ b/apps/browser/src/background/service-factories/devices-api-service.factory.ts @@ -0,0 +1,28 @@ +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; + +import { + ApiServiceInitOptions, + apiServiceFactory, +} from "../../platform/background/service-factories/api-service.factory"; +import { + FactoryOptions, + CachedServices, + factory, +} from "../../platform/background/service-factories/factory-options"; + +type DevicesApiServiceFactoryOptions = FactoryOptions; + +export type DevicesApiServiceInitOptions = DevicesApiServiceFactoryOptions & ApiServiceInitOptions; + +export function devicesApiServiceFactory( + cache: { devicesApiService?: DevicesApiServiceAbstraction } & CachedServices, + opts: DevicesApiServiceInitOptions +): Promise { + return factory( + cache, + "devicesApiService", + opts, + async () => new DevicesApiServiceImplementation(await apiServiceFactory(cache, opts)) + ); +} diff --git a/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts b/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts index 601867ad385..b019db3297d 100644 --- a/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts +++ b/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts @@ -1,13 +1,9 @@ -import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { authServiceFactory, AuthServiceInitOptions, } from "../../auth/background/service-factories/auth-service.factory"; -import { - keyConnectorServiceFactory, - KeyConnectorServiceInitOptions, -} from "../../auth/background/service-factories/key-connector-service.factory"; import { CryptoServiceInitOptions, cryptoServiceFactory, @@ -29,7 +25,7 @@ import { StateServiceInitOptions, stateServiceFactory, } from "../../platform/background/service-factories/state-service.factory"; -import VaultTimeoutService from "../../services/vaultTimeout/vaultTimeout.service"; +import VaultTimeoutService from "../../services/vault-timeout/vault-timeout.service"; import { cipherServiceFactory, CipherServiceInitOptions, @@ -64,7 +60,6 @@ export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions & PlatformUtilsServiceInitOptions & MessagingServiceInitOptions & SearchServiceInitOptions & - KeyConnectorServiceInitOptions & StateServiceInitOptions & AuthServiceInitOptions & VaultTimeoutSettingsServiceInitOptions; @@ -86,7 +81,6 @@ export function vaultTimeoutServiceFactory( await platformUtilsServiceFactory(cache, opts), await messagingServiceFactory(cache, opts), await searchServiceFactory(cache, opts), - await keyConnectorServiceFactory(cache, opts), await stateServiceFactory(cache, opts), await authServiceFactory(cache, opts), await vaultTimeoutSettingsServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts b/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts index 724993127b7..eda86c0a156 100644 --- a/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts +++ b/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts @@ -1,5 +1,5 @@ -import { VaultTimeoutSettingsService as AbstractVaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService as AbstractVaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; import { policyServiceFactory, @@ -9,6 +9,10 @@ import { tokenServiceFactory, TokenServiceInitOptions, } from "../../auth/background/service-factories/token-service.factory"; +import { + userVerificationServiceFactory, + UserVerificationServiceInitOptions, +} from "../../auth/background/service-factories/user-verification-service.factory"; import { CryptoServiceInitOptions, cryptoServiceFactory, @@ -29,7 +33,8 @@ export type VaultTimeoutSettingsServiceInitOptions = VaultTimeoutSettingsService CryptoServiceInitOptions & TokenServiceInitOptions & PolicyServiceInitOptions & - StateServiceInitOptions; + StateServiceInitOptions & + UserVerificationServiceInitOptions; export function vaultTimeoutSettingsServiceFactory( cache: { vaultTimeoutSettingsService?: AbstractVaultTimeoutSettingsService } & CachedServices, @@ -44,7 +49,8 @@ export function vaultTimeoutSettingsServiceFactory( await cryptoServiceFactory(cache, opts), await tokenServiceFactory(cache, opts), await policyServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) + await stateServiceFactory(cache, opts), + await userVerificationServiceFactory(cache, opts) ) ); } diff --git a/apps/browser/src/platform/services/browser-crypto.service.ts b/apps/browser/src/platform/services/browser-crypto.service.ts index 1018c270cb2..a6dce0f39e9 100644 --- a/apps/browser/src/platform/services/browser-crypto.service.ts +++ b/apps/browser/src/platform/services/browser-crypto.service.ts @@ -1,13 +1,32 @@ import { KeySuffixOptions } from "@bitwarden/common/enums"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; export class BrowserCryptoService extends CryptoService { - protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions) { - if (keySuffix === "biometric") { + override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + return await this.stateService.getBiometricUnlock({ userId: userId }); + } + return super.hasUserKeyStored(keySuffix, userId); + } + + /** + * Browser doesn't store biometric keys, so we retrieve them from the desktop and return + * if we successfully saved it into memory as the User Key + */ + protected override async getKeyFromStorage(keySuffix: KeySuffixOptions): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { await this.platformUtilService.authenticateBiometric(); - return (await this.getKey())?.keyB64; + const userKey = await this.stateService.getUserKey(); + if (userKey) { + return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey.keyB64)) as UserKey; + } } - return await super.retrieveKeyFromStorage(keySuffix); + return await super.getKeyFromStorage(keySuffix); } } diff --git a/apps/browser/src/platform/services/browser-state.service.spec.ts b/apps/browser/src/platform/services/browser-state.service.spec.ts index 874e13b7d8f..d6bb83f7fb5 100644 --- a/apps/browser/src/platform/services/browser-state.service.spec.ts +++ b/apps/browser/src/platform/services/browser-state.service.spec.ts @@ -41,7 +41,8 @@ describe("Browser State Service", () => { logService = mock(); stateMigrationService = mock(); stateFactory = mock(); - useAccountCache = true; + // turn off account cache for tests + useAccountCache = false; state = new State(new GlobalState()); state.accounts[userId] = new Account({ diff --git a/apps/browser/src/platform/services/browser-state.service.ts b/apps/browser/src/platform/services/browser-state.service.ts index 37f50d6dc77..34fa1a1d0f3 100644 --- a/apps/browser/src/platform/services/browser-state.service.ts +++ b/apps/browser/src/platform/services/browser-state.service.ts @@ -1,5 +1,12 @@ import { BehaviorSubject } from "rxjs"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { StateMigrationService } from "@bitwarden/common/platform/abstractions/state-migration.service"; +import { + AbstractStorageService, + AbstractMemoryStorageService, +} from "@bitwarden/common/platform/abstractions/storage.service"; +import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { StorageOptions } from "@bitwarden/common/platform/models/domain/storage-options"; import { StateService as BaseStateService } from "@bitwarden/common/platform/services/state.service"; @@ -26,14 +33,44 @@ export class BrowserStateService protected activeAccountSubject: BehaviorSubject; @sessionSync({ initializer: (b: boolean) => b }) protected activeAccountUnlockedSubject: BehaviorSubject; - @sessionSync({ - initializer: Account.fromJSON as any, // TODO: Remove this any when all any types are removed from Account - initializeAs: "record", - }) - protected accountDiskCache: BehaviorSubject>; protected accountDeserializer = Account.fromJSON; + constructor( + storageService: AbstractStorageService, + secureStorageService: AbstractStorageService, + memoryStorageService: AbstractMemoryStorageService, + logService: LogService, + stateMigrationService: StateMigrationService, + stateFactory: StateFactory, + useAccountCache = true + ) { + super( + storageService, + secureStorageService, + memoryStorageService, + logService, + stateMigrationService, + stateFactory, + useAccountCache + ); + + // TODO: This is a hack to fix having a disk cache on both the popup and + // the background page that can get out of sync. We need to work out the + // best way to handle caching with multiple instances of the state service. + if (useAccountCache) { + chrome.storage.onChanged.addListener((changes, namespace) => { + if (namespace === "local") { + for (const key of Object.keys(changes)) { + if (key !== "accountActivity" && this.accountDiskCache.value[key]) { + this.deleteDiskCache(key); + } + } + } + }); + } + } + async addAccount(account: Account) { // Apply browser overrides to default account values account = new Account(account); @@ -132,4 +169,17 @@ export class BrowserStateService this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); } + + // Overriding the base class to prevent deleting the cache on save. We register a storage listener + // to delete the cache in the constructor above. + protected override async saveAccountToDisk( + account: Account, + options: StorageOptions + ): Promise { + const storageLocation = options.useSecureStorage + ? this.secureStorageService + : this.storageService; + + await storageLocation.save(`${options.userId}`, account, options); + } } diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 7da3d3049f7..c15bf86f087 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -1,14 +1,21 @@ import { Injectable, NgModule } from "@angular/core"; import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from "@angular/router"; -import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard"; -import { LockGuard } from "@bitwarden/angular/auth/guards/lock.guard"; -import { UnauthGuard } from "@bitwarden/angular/auth/guards/unauth.guard"; +import { + redirectGuard, + AuthGuard, + lockGuard, + tdeDecryptionRequiredGuard, + UnauthGuard, +} from "@bitwarden/angular/auth/guards"; +import { canAccessFeature } from "@bitwarden/angular/guard/feature-flag.guard"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EnvironmentComponent } from "../auth/popup/environment.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; +import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "../auth/popup/login-with-device.component"; import { LoginComponent } from "../auth/popup/login.component"; import { RegisterComponent } from "../auth/popup/register.component"; @@ -49,8 +56,9 @@ import { TabsComponent } from "./tabs.component"; const routes: Routes = [ { path: "", - redirectTo: "home", pathMatch: "full", + children: [], // Children lets us have an empty component. + canActivate: [redirectGuard({ loggedIn: "/tabs/vault", loggedOut: "/home", locked: "/lock" })], }, { path: "vault", @@ -72,13 +80,19 @@ const routes: Routes = [ { path: "login-with-device", component: LoginWithDeviceComponent, - canActivate: [UnauthGuard], + canActivate: [], + data: { state: "login-with-device" }, + }, + { + path: "admin-approval-requested", + component: LoginWithDeviceComponent, + canActivate: [], data: { state: "login-with-device" }, }, { path: "lock", component: LockComponent, - canActivate: [LockGuard], + canActivate: [lockGuard()], data: { state: "lock" }, }, { @@ -93,6 +107,14 @@ const routes: Routes = [ canActivate: [UnauthGuard], data: { state: "2fa-options" }, }, + { + path: "login-initiated", + component: LoginDecryptionOptionsComponent, + canActivate: [ + tdeDecryptionRequiredGuard(), + canAccessFeature(FeatureFlag.TrustedDeviceEncryption), + ], + }, { path: "sso", component: SsoComponent, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 3665df7decc..f7539d6fa6e 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -20,6 +20,7 @@ import { EnvironmentComponent } from "../auth/popup/environment.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; +import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "../auth/popup/login-with-device.component"; import { LoginComponent } from "../auth/popup/login.component"; import { RegisterComponent } from "../auth/popup/register.component"; @@ -121,6 +122,7 @@ import "../platform/popup/locales"; LockComponent, LoginComponent, LoginWithDeviceComponent, + LoginDecryptionOptionsComponent, OptionsComponent, GeneratorComponent, PasswordGeneratorHistoryComponent, diff --git a/apps/browser/src/popup/components/user-verification.component.html b/apps/browser/src/popup/components/user-verification.component.html index 8d7f1ed8706..25bd81cf394 100644 --- a/apps/browser/src/popup/components/user-verification.component.html +++ b/apps/browser/src/popup/components/user-verification.component.html @@ -1,4 +1,4 @@ - +
- +
- +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+

{{ "loggingInAs" | i18n }} {{ data.userEmail }}

+ {{ "notYou" | i18n }} +
+ +
+
diff --git a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..f64ec977ce7 --- /dev/null +++ b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,19 @@ +import { Component } from "@angular/core"; + +import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; + +@Component({ + selector: "desktop-login-decryption-options", + templateUrl: "login-decryption-options.component.html", +}) +export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { + override async createUser(): Promise { + try { + await super.createUser(); + this.messagingService.send("redrawMenu"); + await this.router.navigate(["/vault"]); + } catch (error) { + this.validationService.showError(error); + } + } +} diff --git a/apps/desktop/src/auth/login/login-with-device.component.html b/apps/desktop/src/auth/login/login-with-device.component.html index 29fb1034942..a81c92ad1d1 100644 --- a/apps/desktop/src/auth/login/login-with-device.component.html +++ b/apps/desktop/src/auth/login/login-with-device.component.html @@ -1,40 +1,72 @@
Bitwarden -

{{ "logInInitiated" | i18n }}

-
-
-
-
-

{{ "notificationSentDevice" | i18n }}

-

- {{ "fingerprintMatchInfo" | i18n }} -

-
+ +

{{ "loginInitiated" | i18n }}

-
-

{{ "fingerprintPhraseHeader" | i18n }}

- {{ fingerprintPhrase }} -
+
+
+
+
+

{{ "notificationSentDevice" | i18n }}

+

+ {{ "fingerprintMatchInfo" | i18n }} +

+
- +
+

{{ "fingerprintPhraseHeader" | i18n }}

+ {{ fingerprintPhrase }} +
-
-

- {{ "needAnotherOption" | i18n }} - - {{ "viewAllLoginOptions" | i18n }} - -

+ + +
+

+ {{ "needAnotherOption" | i18n }} + + {{ "viewAllLoginOptions" | i18n }} + +

+
-
+
+ + +

{{ "adminApprovalRequested" | i18n }}

+ +
+
+
+
+

{{ "adminApprovalRequestSentToAdmins" | i18n }}

+

{{ "youWillBeNotifiedOnceApproved" | i18n }}

+
+ +
+

{{ "fingerprintPhraseHeader" | i18n }}

+ {{ fingerprintPhrase }} +
+ +
+

+ {{ "troubleLoggingIn" | i18n }} + + {{ "viewAllLoginOptions" | i18n }} + +

+
+
+
+
+
diff --git a/apps/desktop/src/auth/login/login-with-device.component.ts b/apps/desktop/src/auth/login/login-with-device.component.ts index 3b3ee83ae49..daca2d19936 100644 --- a/apps/desktop/src/auth/login/login-with-device.component.ts +++ b/apps/desktop/src/auth/login/login-with-device.component.ts @@ -1,3 +1,4 @@ +import { Location } from "@angular/common"; import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { Router } from "@angular/router"; @@ -5,7 +6,9 @@ import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwa import { ModalService } from "@bitwarden/angular/services/modal.service"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -50,7 +53,10 @@ export class LoginWithDeviceComponent private modalService: ModalService, syncService: SyncService, stateService: StateService, - loginService: LoginService + loginService: LoginService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + authReqCryptoService: AuthRequestCryptoServiceAbstraction, + private location: Location ) { super( router, @@ -67,7 +73,9 @@ export class LoginWithDeviceComponent anonymousHubService, validationService, stateService, - loginService + loginService, + deviceTrustCryptoService, + authReqCryptoService ); super.onSuccessfulLogin = () => { @@ -100,7 +108,7 @@ export class LoginWithDeviceComponent super.ngOnDestroy(); } - goToLogin() { - this.router.navigate(["/login"]); + back() { + this.location.back(); } } diff --git a/apps/desktop/src/auth/login/login.component.ts b/apps/desktop/src/auth/login/login.component.ts index 00aa0300f61..45b330f6dae 100644 --- a/apps/desktop/src/auth/login/login.component.ts +++ b/apps/desktop/src/auth/login/login.component.ts @@ -7,8 +7,8 @@ import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; import { ModalService } from "@bitwarden/angular/services/modal.service"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; diff --git a/apps/desktop/src/auth/login/login.module.ts b/apps/desktop/src/auth/login/login.module.ts index 35ad83e9db0..9fb2b28915a 100644 --- a/apps/desktop/src/auth/login/login.module.ts +++ b/apps/desktop/src/auth/login/login.module.ts @@ -5,12 +5,18 @@ import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components import { SharedModule } from "../../app/shared/shared.module"; +import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "./login-with-device.component"; import { LoginComponent } from "./login.component"; @NgModule({ imports: [SharedModule, RouterModule], - declarations: [LoginComponent, LoginWithDeviceComponent, EnvironmentSelectorComponent], + declarations: [ + LoginComponent, + LoginWithDeviceComponent, + EnvironmentSelectorComponent, + LoginDecryptionOptionsComponent, + ], exports: [LoginComponent, LoginWithDeviceComponent], }) export class LoginModule {} diff --git a/apps/desktop/src/auth/sso.component.ts b/apps/desktop/src/auth/sso.component.ts index 75f380b0ed3..22badf9d69d 100644 --- a/apps/desktop/src/auth/sso.component.ts +++ b/apps/desktop/src/auth/sso.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router"; import { SsoComponent as BaseSsoComponent } from "@bitwarden/angular/auth/components/sso.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -30,7 +31,8 @@ export class SsoComponent extends BaseSsoComponent { cryptoFunctionService: CryptoFunctionService, environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationServiceAbstraction, - logService: LogService + logService: LogService, + configService: ConfigServiceAbstraction ) { super( authService, @@ -43,11 +45,17 @@ export class SsoComponent extends BaseSsoComponent { cryptoFunctionService, environmentService, passwordGenerationService, - logService + logService, + configService ); - super.onSuccessfulLogin = () => { - return syncService.fullSync(true); + super.onSuccessfulLogin = async () => { + syncService.fullSync(true); }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); + }; + this.redirectUri = "bitwarden://sso-callback"; this.clientId = "desktop"; } diff --git a/apps/desktop/src/auth/two-factor.component.ts b/apps/desktop/src/auth/two-factor.component.ts index ee3764027a5..bf3545820b9 100644 --- a/apps/desktop/src/auth/two-factor.component.ts +++ b/apps/desktop/src/auth/two-factor.component.ts @@ -1,7 +1,8 @@ -import { Component, ViewChild, ViewContainerRef } from "@angular/core"; +import { Component, Inject, ViewChild, ViewContainerRef } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -9,6 +10,7 @@ import { LoginService } from "@bitwarden/common/auth/abstractions/login.service" import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -43,7 +45,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { logService: LogService, twoFactorService: TwoFactorService, appIdService: AppIdService, - loginService: LoginService + loginService: LoginService, + configService: ConfigServiceAbstraction, + @Inject(WINDOW) protected win: Window ) { super( authService, @@ -51,18 +55,22 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { i18nService, apiService, platformUtilsService, - window, + win, environmentService, stateService, route, logService, twoFactorService, appIdService, - loginService + loginService, + configService ); - super.onSuccessfulLogin = () => { - this.loginService.clearValues(); - return syncService.fullSync(true); + super.onSuccessfulLogin = async () => { + syncService.fullSync(true); + }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); }; } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index ab8446ee465..eba53b45b38 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1492,6 +1492,9 @@ "vaultTimeoutActionLogOutDesc": { "message": "Re-authentication is required to access your vault again." }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, "lock": { "message": "Lock", "description": "Verb form: to make secure or inaccesible by" @@ -2106,8 +2109,8 @@ "logInWithAnotherDevice": { "message": "Log in with another device" }, - "logInInitiated": { - "message": "Log in initiated" + "loginInitiated": { + "message": "Login initiated" }, "notificationSentDevice": { "message": "A notification has been sent to your device." @@ -2246,6 +2249,34 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "deviceApprovalRequired": { + "message": "Device approval required. Select an approval option below:" + }, + "rememberThisDevice": { + "message": "Remember this device" + }, + "uncheckIfPublicDevice": { + "message": "Uncheck if using a public device" + }, + "approveFromYourOtherDevice": { + "message": "Approve from your other device" + }, + "requestAdminApproval": { + "message": "Request admin approval" + }, + "approveWithMasterPassword": { + "message": "Approve with master password" + }, + "region": { + "message": "Region" + }, + "ssoIdentifierRequired": { + "message": "Organization SSO identifier is required." + }, + "eu": { + "message": "EU", + "description": "European Union" + }, "loggingInOn": { "message": "Logging in on" }, @@ -2260,5 +2291,29 @@ }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." + }, + "accountSuccessfullyCreated": { + "message": "Account successfully created!" + }, + "adminApprovalRequested": { + "message": "Admin approval requested" + }, + "adminApprovalRequestSentToAdmins": { + "message": "Your request has been sent to your admin." + }, + "youWillBeNotifiedOnceApproved": { + "message": "You will be notified once approved." + }, + "troubleLoggingIn": { + "message": "Trouble logging in?" + }, + "loginApproved": { + "message": "Login approved" + }, + "userEmailMissing": { + "message": "User email missing" + }, + "deviceTrusted": { + "message": "Device trusted" } } diff --git a/apps/desktop/src/main/menu/menu.account.ts b/apps/desktop/src/main/menu/menu.account.ts index 10a6d6d77d5..5060a4934c5 100644 --- a/apps/desktop/src/main/menu/menu.account.ts +++ b/apps/desktop/src/main/menu/menu.account.ts @@ -15,14 +15,15 @@ export class AccountMenu implements IMenubarMenu { } get items(): MenuItemConstructorOptions[] { - return [ - this.premiumMembership, - this.changeMasterPassword, - this.twoStepLogin, - this.fingerprintPhrase, - this.separator, - this.deleteAccount, - ]; + const items = [this.premiumMembership]; + if (this._hasMasterPassword) { + items.push(this.changeMasterPassword); + } + items.push(this.twoStepLogin); + items.push(this.fingerprintPhrase); + items.push(this.separator); + items.push(this.deleteAccount); + return items; } private readonly _i18nService: I18nService; @@ -30,19 +31,22 @@ export class AccountMenu implements IMenubarMenu { private readonly _webVaultUrl: string; private readonly _window: BrowserWindow; private readonly _isLocked: boolean; + private readonly _hasMasterPassword: boolean; constructor( i18nService: I18nService, messagingService: MessagingService, webVaultUrl: string, window: BrowserWindow, - isLocked: boolean + isLocked: boolean, + hasMasterPassword: boolean ) { this._i18nService = i18nService; this._messagingService = messagingService; this._webVaultUrl = webVaultUrl; this._window = window; this._isLocked = isLocked; + this._hasMasterPassword = hasMasterPassword; } private get premiumMembership(): MenuItemConstructorOptions { diff --git a/apps/desktop/src/main/menu/menu.bitwarden.ts b/apps/desktop/src/main/menu/menu.bitwarden.ts index 3c3a16702ee..6873f679de7 100644 --- a/apps/desktop/src/main/menu/menu.bitwarden.ts +++ b/apps/desktop/src/main/menu/menu.bitwarden.ts @@ -52,9 +52,10 @@ export class BitwardenMenu extends FirstMenu implements IMenubarMenu { updater: UpdaterMain, window: BrowserWindow, accounts: { [userId: string]: MenuAccount }, - isLocked: boolean + isLocked: boolean, + isLockable: boolean ) { - super(i18nService, messagingService, updater, window, accounts, isLocked); + super(i18nService, messagingService, updater, window, accounts, isLocked, isLockable); } private get aboutBitwarden(): MenuItemConstructorOptions { diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index 618acdc7fe1..173b6066aba 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -51,9 +51,10 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { updater: UpdaterMain, window: BrowserWindow, accounts: { [userId: string]: MenuAccount }, - isLocked: boolean + isLocked: boolean, + isLockable: boolean ) { - super(i18nService, messagingService, updater, window, accounts, isLocked); + super(i18nService, messagingService, updater, window, accounts, isLocked, isLockable); } private get addNewLogin(): MenuItemConstructorOptions { diff --git a/apps/desktop/src/main/menu/menu.first.ts b/apps/desktop/src/main/menu/menu.first.ts index 805956f2632..b164e280d0b 100644 --- a/apps/desktop/src/main/menu/menu.first.ts +++ b/apps/desktop/src/main/menu/menu.first.ts @@ -9,33 +9,24 @@ import { UpdaterMain } from "../updater.main"; import { MenuAccount } from "./menu.updater"; export class FirstMenu { - protected readonly _i18nService: I18nService; - protected readonly _updater: UpdaterMain; - protected readonly _messagingService: MessagingService; - protected readonly _accounts: { [userId: string]: MenuAccount }; - protected readonly _window: BrowserWindow; - protected readonly _isLocked: boolean; - constructor( - i18nService: I18nService, - messagingService: MessagingService, - updater: UpdaterMain, - window: BrowserWindow, - accounts: { [userId: string]: MenuAccount }, - isLocked: boolean - ) { - this._i18nService = i18nService; - this._updater = updater; - this._messagingService = messagingService; - this._window = window; - this._accounts = accounts; - this._isLocked = isLocked; - } + protected readonly _i18nService: I18nService, + protected readonly _messagingService: MessagingService, + protected readonly _updater: UpdaterMain, + protected readonly _window: BrowserWindow, + protected readonly _accounts: { [userId: string]: MenuAccount }, + protected readonly _isLocked: boolean, + protected readonly _isLockable: boolean + ) {} protected get hasAccounts(): boolean { return this._accounts != null && Object.keys(this._accounts).length > 0; } + protected get hasLockableAccounts(): boolean { + return this._accounts != null && Object.values(this._accounts).some((a) => a.isLockable); + } + protected get checkForUpdates(): MenuItemConstructorOptions { return { id: "checkForUpdates", @@ -66,23 +57,29 @@ export class FirstMenu { id: "lock", label: this.localize("lockVault"), submenu: this.lockSubmenu, - enabled: this.hasAccounts, + enabled: this.hasLockableAccounts, }; } protected get lockSubmenu(): MenuItemConstructorOptions[] { const value: MenuItemConstructorOptions[] = []; for (const userId in this._accounts) { - if (userId == null) { + if (!userId) { + continue; + } + + const account = this._accounts[userId]; + + if (account == null || !account.isLockable) { continue; } value.push({ - label: this._accounts[userId].email, - id: `lockNow_${this._accounts[userId].userId}`, - click: () => this.sendMessage("lockVault", { userId: this._accounts[userId].userId }), - enabled: !this._accounts[userId].isLocked, - visible: this._accounts[userId].isAuthenticated, + label: account.email, + id: `lockNow_${account.userId}`, + click: () => this.sendMessage("lockVault", { userId: account.userId }), + enabled: !account.isLocked, + visible: account.isAuthenticated, }); } return value; diff --git a/apps/desktop/src/main/menu/menu.updater.ts b/apps/desktop/src/main/menu/menu.updater.ts index 75454a56f86..170804cae25 100644 --- a/apps/desktop/src/main/menu/menu.updater.ts +++ b/apps/desktop/src/main/menu/menu.updater.ts @@ -1,5 +1,4 @@ export class MenuUpdateRequest { - hideChangeMasterPassword: boolean; activeUserId: string; accounts: { [userId: string]: MenuAccount }; } @@ -7,6 +6,8 @@ export class MenuUpdateRequest { export class MenuAccount { isAuthenticated: boolean; isLocked: boolean; + isLockable: boolean; userId: string; email: string; + hasMasterPassword: boolean; } diff --git a/apps/desktop/src/main/menu/menubar.ts b/apps/desktop/src/main/menu/menubar.ts index 6ac28a1c8db..c3f37ecbffb 100644 --- a/apps/desktop/src/main/menu/menubar.ts +++ b/apps/desktop/src/main/menu/menubar.ts @@ -62,6 +62,10 @@ export class Menubar { isLocked = updateRequest.accounts[updateRequest.activeUserId]?.isLocked ?? true; } + const isLockable = !isLocked && updateRequest?.accounts[updateRequest.activeUserId]?.isLockable; + const hasMasterPassword = + updateRequest?.accounts[updateRequest.activeUserId]?.hasMasterPassword ?? false; + this.items = [ new FileMenu( i18nService, @@ -69,11 +73,19 @@ export class Menubar { updaterMain, windowMain.win, updateRequest?.accounts, - isLocked + isLocked, + isLockable ), new EditMenu(i18nService, messagingService, isLocked), new ViewMenu(i18nService, messagingService, isLocked), - new AccountMenu(i18nService, messagingService, webVaultUrl, windowMain.win, isLocked), + new AccountMenu( + i18nService, + messagingService, + webVaultUrl, + windowMain.win, + isLocked, + hasMasterPassword + ), new WindowMenu(i18nService, messagingService, windowMain), new HelpMenu( i18nService, @@ -91,7 +103,8 @@ export class Menubar { updaterMain, windowMain.win, updateRequest?.accounts, - isLocked + isLocked, + isLockable ), ], ...this.items, diff --git a/apps/desktop/src/platform/services/electron-crypto.service.spec.ts b/apps/desktop/src/platform/services/electron-crypto.service.spec.ts new file mode 100644 index 00000000000..92f9391b131 --- /dev/null +++ b/apps/desktop/src/platform/services/electron-crypto.service.spec.ts @@ -0,0 +1,91 @@ +import { mock, mockReset } from "jest-mock-extended"; + +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; + +import { ElectronCryptoService } from "./electron-crypto.service"; +import { ElectronStateService } from "./electron-state.service.abstraction"; + +describe("electronCryptoService", () => { + let electronCryptoService: ElectronCryptoService; + + const cryptoFunctionService = mock(); + const encryptService = mock(); + const platformUtilService = mock(); + const logService = mock(); + const stateService = mock(); + + const mockUserId = "mock user id"; + + beforeEach(() => { + mockReset(cryptoFunctionService); + mockReset(encryptService); + mockReset(platformUtilService); + mockReset(logService); + mockReset(stateService); + + electronCryptoService = new ElectronCryptoService( + cryptoFunctionService, + encryptService, + platformUtilService, + logService, + stateService + ); + }); + + it("instantiates", () => { + expect(electronCryptoService).not.toBeFalsy(); + }); + + describe("setUserKey", () => { + let mockUserKey: UserKey; + + beforeEach(() => { + const mockRandomBytes = new Uint8Array(64) as CsprngArray; + mockUserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey; + }); + + describe("Biometric Key refresh", () => { + it("sets an Biometric key if getBiometricUnlock is true and the platform supports secure storage", async () => { + stateService.getBiometricUnlock.mockResolvedValue(true); + platformUtilService.supportsSecureStorage.mockReturnValue(true); + stateService.getBiometricRequirePasswordOnStart.mockResolvedValue(false); + + await electronCryptoService.setUserKey(mockUserKey, mockUserId); + + expect(stateService.setUserKeyBiometric).toHaveBeenCalledWith( + expect.objectContaining({ key: expect.any(String), clientEncKeyHalf: null }), + { + userId: mockUserId, + } + ); + }); + + it("clears the Biometric key if getBiometricUnlock is false or the platform does not support secure storage", async () => { + stateService.getBiometricUnlock.mockResolvedValue(true); + platformUtilService.supportsSecureStorage.mockReturnValue(false); + + await electronCryptoService.setUserKey(mockUserKey, mockUserId); + + expect(stateService.setUserKeyBiometric).toHaveBeenCalledWith(null, { + userId: mockUserId, + }); + }); + + it("clears the old deprecated Biometric key whenever a User Key is set", async () => { + await electronCryptoService.setUserKey(mockUserKey, mockUserId); + + expect(stateService.setCryptoMasterKeyBiometric).toHaveBeenCalledWith(null, { + userId: mockUserId, + }); + }); + }); + }); +}); diff --git a/apps/desktop/src/platform/services/electron-crypto.service.ts b/apps/desktop/src/platform/services/electron-crypto.service.ts index 1fb90e52ca4..e21d2001977 100644 --- a/apps/desktop/src/platform/services/electron-crypto.service.ts +++ b/apps/desktop/src/platform/services/electron-crypto.service.ts @@ -4,7 +4,12 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt. import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { + MasterKey, + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; import { CsprngString } from "@bitwarden/common/types/csprng"; @@ -21,36 +26,80 @@ export class ElectronCryptoService extends CryptoService { super(cryptoFunctionService, encryptService, platformUtilsService, logService, stateService); } - protected override async storeKey(key: SymmetricCryptoKey, userId?: string) { - await super.storeKey(key, userId); + override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3474) + const oldKey = await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId }); + return oldKey || (await this.stateService.hasUserKeyBiometric({ userId: userId })); + } + return super.hasUserKeyStored(keySuffix, userId); + } + + override async clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + this.stateService.setUserKeyBiometric(null, { userId: userId }); + this.clearDeprecatedKeys(KeySuffixOptions.Biometric, userId); + return; + } + super.clearStoredUserKey(keySuffix, userId); + } + + protected override async storeAdditionalKeys(key: UserKey, userId?: string) { + await super.storeAdditionalKeys(key, userId); const storeBiometricKey = await this.shouldStoreKey(KeySuffixOptions.Biometric, userId); if (storeBiometricKey) { await this.storeBiometricKey(key, userId); } else { - await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + await this.stateService.setUserKeyBiometric(null, { userId: userId }); } + await this.clearDeprecatedKeys(KeySuffixOptions.Biometric, userId); } - protected async storeBiometricKey(key: SymmetricCryptoKey, userId?: string): Promise { + protected override async getKeyFromStorage( + keySuffix: KeySuffixOptions, + userId?: string + ): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + await this.migrateBiometricKeyIfNeeded(userId); + const userKey = await this.stateService.getUserKeyBiometric({ userId: userId }); + return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey)) as UserKey; + } + return await super.getKeyFromStorage(keySuffix, userId); + } + + protected async storeBiometricKey(key: UserKey, userId?: string): Promise { let clientEncKeyHalf: CsprngString = null; if (await this.stateService.getBiometricRequirePasswordOnStart({ userId })) { clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userId); } - await this.stateService.setCryptoMasterKeyBiometric( + await this.stateService.setUserKeyBiometric( { key: key.keyB64, clientEncKeyHalf }, { userId: userId } ); } + protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + const biometricUnlock = await this.stateService.getBiometricUnlock({ userId: userId }); + return biometricUnlock && this.platformUtilService.supportsSecureStorage(); + } + return await super.shouldStoreKey(keySuffix, userId); + } + + protected override async clearAllStoredUserKeys(userId?: string): Promise { + await this.stateService.setUserKeyBiometric(null, { userId: userId }); + super.clearAllStoredUserKeys(userId); + } + private async getBiometricEncryptionClientKeyHalf(userId?: string): Promise { try { let biometricKey = await this.stateService .getBiometricEncryptionClientKeyHalf({ userId }) .then((result) => result?.decrypt(null /* user encrypted */)) .then((result) => result as CsprngString); - const userKey = await this.getKeyForUserEncryption(); + const userKey = await this.getUserKeyWithLegacySupport(); if (biometricKey == null && userKey != null) { const keyBytes = await this.cryptoFunctionService.randomBytes(32); biometricKey = Utils.fromBufferToUtf8(keyBytes) as CsprngString; @@ -63,4 +112,34 @@ export class ElectronCryptoService extends CryptoService { return null; } } + + // --LEGACY METHODS-- + // We previously used the master key for additional keys, but now we use the user key. + // These methods support migrating the old keys to the new ones. + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3475) + + override async clearDeprecatedKeys(keySuffix: KeySuffixOptions, userId?: string) { + if (keySuffix === KeySuffixOptions.Biometric) { + await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + } + + super.clearDeprecatedKeys(keySuffix, userId); + } + + private async migrateBiometricKeyIfNeeded(userId?: string) { + if (await this.stateService.hasCryptoMasterKeyBiometric({ userId })) { + const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId }); + // decrypt + const masterKey = new SymmetricCryptoKey(Utils.fromB64ToArray(oldBiometricKey)) as MasterKey; + let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey(); + encUserKey = encUserKey ?? (await this.stateService.getMasterKeyEncryptedUserKey()); + if (!encUserKey) { + throw new Error("No user key found during biometric migration"); + } + const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey)); + // migrate + await this.storeBiometricKey(userKey, userId); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId }); + } + } } diff --git a/apps/desktop/src/platform/services/electron-state.service.ts b/apps/desktop/src/platform/services/electron-state.service.ts index 334cab96698..0503aeb52a3 100644 --- a/apps/desktop/src/platform/services/electron-state.service.ts +++ b/apps/desktop/src/platform/services/electron-state.service.ts @@ -98,6 +98,10 @@ export class ElectronStateService options ); + if (b64DeviceKey == null) { + return null; + } + return new SymmetricCryptoKey(Utils.fromB64ToArray(b64DeviceKey)) as DeviceKey; } diff --git a/apps/desktop/src/scss/pages.scss b/apps/desktop/src/scss/pages.scss index 5b3ad4a8583..fda75e834f3 100644 --- a/apps/desktop/src/scss/pages.scss +++ b/apps/desktop/src/scss/pages.scss @@ -5,7 +5,8 @@ #lock-page, #sso-page, #set-password-page, -#remove-password-page { +#remove-password-page, +#login-decryption-options-page { display: flex; justify-content: center; align-items: center; @@ -53,7 +54,8 @@ #hint-page, #two-factor-page, #lock-page, -#update-temp-password-page { +#update-temp-password-page, +#login-decryption-options-page { .content { width: 325px; transition: width 0.25s linear; @@ -189,7 +191,8 @@ } #login-page, -#login-with-device-page { +#login-with-device-page, +#login-decryption-options-page { flex-direction: column; justify-content: unset; padding-top: 20px; @@ -273,3 +276,14 @@ } } } + +#login-decryption-options-page { + .standard-bottom-margin { + margin-bottom: 20px; + } + + #rememberThisDeviceHintText { + font-size: $font-size-small; + color: $text-muted; + } +} diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts index 644dfe8f906..9f5f1d460df 100644 --- a/apps/desktop/src/services/native-message-handler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -8,7 +8,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { StateService } from "@bitwarden/common/platform/services/state.service"; @@ -144,7 +144,9 @@ export class NativeMessageHandlerService { } private async handleEncryptedMessage(message: EncryptedMessage) { - message.encryptedCommand = EncString.fromJSON(message.encryptedCommand.toString()); + message.encryptedCommand = EncString.fromJSON( + message.encryptedCommand.toString() as EncryptedString + ); const decryptedCommandData = await this.decryptPayload(message); const { command } = decryptedCommandData; diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 7647410fd75..3928778f313 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -136,14 +136,23 @@ export class NativeMessagingService { }); } - const key = await this.cryptoService.getKeyFromStorage( + const userKey = await this.cryptoService.getUserKeyFromStorage( KeySuffixOptions.Biometric, message.userId ); + const masterKey = await this.cryptoService.getMasterKey(message.userId); - if (key != null) { + if (userKey != null) { + // we send the master key still for backwards compatibility + // with older browser extensions + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472) this.send( - { command: "biometricUnlock", response: "unlocked", keyB64: key.keyB64 }, + { + command: "biometricUnlock", + response: "unlocked", + keyB64: masterKey?.keyB64, + userKeyB64: userKey.keyB64, + }, appId ); } else { 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 index 31ced8aee10..ae754faabe7 100644 --- 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 @@ -7,6 +7,7 @@ import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enum import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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 { BulkUserDetails } from "./bulk-status.component"; @@ -98,7 +99,7 @@ export class BulkConfirmComponent implements OnInit { ); } - protected getCryptoKey() { + protected getCryptoKey(): Promise { return this.cryptoService.getOrgKey(this.organizationId); } diff --git a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts index 5d2a5f54bad..7d0fae37e0a 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts @@ -22,7 +22,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { DialogService } from "@bitwarden/components"; @@ -171,26 +174,32 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { orgSymKey ); - // Decrypt User's Reset Password Key to get EncKey + // Decrypt User's Reset Password Key to get UserKey const decValue = await this.cryptoService.rsaDecrypt(resetPasswordKey, decPrivateKey); - const userEncKey = new SymmetricCryptoKey(decValue); + const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey; - // Create new key and hash new password - const newKey = await this.cryptoService.makeKey( + // Create new master key and hash new password + const newMasterKey = await this.cryptoService.makeMasterKey( this.newPassword, this.email.trim().toLowerCase(), kdfType, new KdfConfig(kdfIterations, kdfMemory, kdfParallelism) ); - const newPasswordHash = await this.cryptoService.hashPassword(this.newPassword, newKey); + const newMasterKeyHash = await this.cryptoService.hashMasterKey( + this.newPassword, + newMasterKey + ); - // Create new encKey for the User - const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey); + // Create new encrypted user key for the User + const newUserKey = await this.cryptoService.encryptUserKeyWithMasterKey( + newMasterKey, + existingUserKey + ); // Create request const request = new OrganizationUserResetPasswordRequest(); - request.key = newEncKey[1].encryptedString; - request.newMasterPasswordHash = newPasswordHash; + request.key = newUserKey[1].encryptedString; + request.newMasterPasswordHash = newMasterKeyHash; // Change user's password return this.organizationUserService.putOrganizationUserResetPassword( diff --git a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts index dca1b18530a..c4ca1dc241f 100644 --- a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; -import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard"; +import { AuthGuard } from "@bitwarden/angular/auth/guards"; import { canAccessOrgAdmin, canAccessGroupsTab, diff --git a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts index a533967d8f5..535d7d375ab 100644 --- a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts +++ b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts @@ -58,8 +58,8 @@ export class EnrollMasterPasswordReset { const publicKey = Utils.fromB64ToArray(orgKeys.publicKey); // RSA Encrypt user's encKey.key with organization public key - const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); + const userKey = await this.cryptoService.getUserKey(); + const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey); keyString = encryptedKey.encryptedString; toastStringRef = "enrollPasswordResetSuccess"; diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 8ce51df9e6f..3066dbe093c 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -11,7 +11,7 @@ import { EventUploadService } from "@bitwarden/common/abstractions/event/event-u import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; diff --git a/apps/web/src/app/auth/accept-organization.component.ts b/apps/web/src/app/auth/accept-organization.component.ts index bdc3511f760..ef2dda138dd 100644 --- a/apps/web/src/app/auth/accept-organization.component.ts +++ b/apps/web/src/app/auth/accept-organization.component.ts @@ -18,6 +18,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrgKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { BaseAcceptComponent } from "../common/base.accept.component"; @@ -108,16 +109,14 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { const request = new OrganizationUserAcceptInitRequest(); request.token = qParams.token; - const [encryptedOrgShareKey, orgShareKey] = await this.cryptoService.makeShareKey(); - const [orgPublicKey, encryptedOrgPrivateKey] = await this.cryptoService.makeKeyPair( - orgShareKey - ); + const [encryptedOrgKey, orgKey] = await this.cryptoService.makeOrgKey(); + const [orgPublicKey, encryptedOrgPrivateKey] = await this.cryptoService.makeKeyPair(orgKey); const collection = await this.cryptoService.encrypt( this.i18nService.t("defaultCollection"), - orgShareKey + orgKey ); - request.key = encryptedOrgShareKey.encryptedString; + request.key = encryptedOrgKey.encryptedString; request.keys = new OrganizationKeysRequest( orgPublicKey, encryptedOrgPrivateKey.encryptedString @@ -141,8 +140,8 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { const publicKey = Utils.fromB64ToArray(response.publicKey); // RSA Encrypt user's encKey.key with organization public key - const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); + const userKey = await this.cryptoService.getUserKey(); + const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey); // Add reset password key to accept request request.resetPasswordKey = encryptedKey.encryptedString; diff --git a/apps/web/src/app/auth/lock.component.ts b/apps/web/src/app/auth/lock.component.ts index 185ce241d42..1cb97fbd558 100644 --- a/apps/web/src/app/auth/lock.component.ts +++ b/apps/web/src/app/auth/lock.component.ts @@ -3,11 +3,12 @@ import { Router } from "@angular/router"; import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -38,12 +39,13 @@ export class LockComponent extends BaseLockComponent { stateService: StateService, apiService: ApiService, logService: LogService, - keyConnectorService: KeyConnectorService, ngZone: NgZone, policyApiService: PolicyApiServiceAbstraction, policyService: InternalPolicyService, passwordStrengthService: PasswordStrengthServiceAbstraction, - dialogService: DialogService + dialogService: DialogService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + userVerificationService: UserVerificationService ) { super( router, @@ -57,12 +59,13 @@ export class LockComponent extends BaseLockComponent { stateService, apiService, logService, - keyConnectorService, ngZone, policyApiService, policyService, passwordStrengthService, - dialogService + dialogService, + deviceTrustCryptoService, + userVerificationService ); } diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html new file mode 100644 index 00000000000..ed59cc12388 --- /dev/null +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html @@ -0,0 +1,105 @@ +
+
+
+ +
+ + +

+ + {{ "loading" | i18n }} +

+
+ +
+ +

{{ "loginInitiated" | i18n }}

+ +

+ {{ "deviceApprovalRequired" | i18n }} +

+ +
+ + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + +
+ +
+ + + + + +
+
+ + +

{{ "loggedInExclamation" | i18n }}

+ +
+ + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + +
+ + +
+ +
+ +
+

{{ "loggingInAs" | i18n }} {{ data.userEmail }}

+ {{ "notYou" | i18n }} +
+
+
+
diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..2c97bd227f9 --- /dev/null +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,21 @@ +import { Component } from "@angular/core"; + +import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; +@Component({ + selector: "web-login-decryption-options", + templateUrl: "login-decryption-options.component.html", +}) +export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { + override async createUser(): Promise { + try { + await super.createUser(); + await this.router.navigate(["/vault"]); + } catch (error) { + this.validationService.showError(error); + } + } + + createUserAction = async (): Promise => { + return this.createUser(); + }; +} diff --git a/apps/web/src/app/auth/login/login-with-device.component.html b/apps/web/src/app/auth/login/login-with-device.component.html index f190f8f5c6e..80811ac8b64 100644 --- a/apps/web/src/app/auth/login/login-with-device.component.html +++ b/apps/web/src/app/auth/login/login-with-device.component.html @@ -5,42 +5,71 @@ >
-

- {{ "loginOrCreateNewAccount" | i18n }} -

-
-

{{ "logInInitiated" | i18n }}

+ +

+ {{ "loginOrCreateNewAccount" | i18n }} +

-
-

{{ "notificationSentDevice" | i18n }}

+
+

{{ "loginInitiated" | i18n }}

-

- {{ "fingerprintMatchInfo" | i18n }} -

+
+

{{ "notificationSentDevice" | i18n }}

+ +

+ {{ "fingerprintMatchInfo" | i18n }} +

+
+ +
+

{{ "fingerprintPhraseHeader" | i18n }}

+

+ {{ fingerprintPhrase }} +

+
+ + + +
+ +
+ {{ "loginWithDeviceEnabledNote" | i18n }} + {{ "viewAllLoginOptions" | i18n }} +
+ + +
+

{{ "adminApprovalRequested" | i18n }}

-
-

{{ "fingerprintPhraseHeader" | i18n }}

-

- {{ fingerprintPhrase }} -

+
+

{{ "adminApprovalRequestSentToAdmins" | i18n }}

+

{{ "youWillBeNotifiedOnceApproved" | i18n }}

+
+ +
+

{{ "fingerprintPhraseHeader" | i18n }}

+

+ {{ fingerprintPhrase }} +

+
+ +
+ +
+ {{ "troubleLoggingIn" | i18n }} + {{ "viewAllLoginOptions" | i18n }} +
- - - -
- -
- {{ "loginWithDeviceEnabledNote" | i18n }} - {{ "viewAllLoginOptions" | i18n }} -
-
+
diff --git a/apps/web/src/app/auth/login/login-with-device.component.ts b/apps/web/src/app/auth/login/login-with-device.component.ts index c5e73bf04b1..ff66bdb886b 100644 --- a/apps/web/src/app/auth/login/login-with-device.component.ts +++ b/apps/web/src/app/auth/login/login-with-device.component.ts @@ -4,7 +4,9 @@ import { Router } from "@angular/router"; import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -41,7 +43,9 @@ export class LoginWithDeviceComponent anonymousHubService: AnonymousHubService, validationService: ValidationService, stateService: StateService, - loginService: LoginService + loginService: LoginService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + authReqCryptoService: AuthRequestCryptoServiceAbstraction ) { super( router, @@ -58,7 +62,9 @@ export class LoginWithDeviceComponent anonymousHubService, validationService, stateService, - loginService + loginService, + deviceTrustCryptoService, + authReqCryptoService ); } } diff --git a/apps/web/src/app/auth/login/login.component.ts b/apps/web/src/app/auth/login/login.component.ts index 316b353c490..3bc65542b73 100644 --- a/apps/web/src/app/auth/login/login.component.ts +++ b/apps/web/src/app/auth/login/login.component.ts @@ -6,7 +6,6 @@ import { first } from "rxjs/operators"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; @@ -14,6 +13,7 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; diff --git a/apps/web/src/app/auth/login/login.module.ts b/apps/web/src/app/auth/login/login.module.ts index b01fa015888..2a074ca2a61 100644 --- a/apps/web/src/app/auth/login/login.module.ts +++ b/apps/web/src/app/auth/login/login.module.ts @@ -4,12 +4,13 @@ import { CheckboxModule } from "@bitwarden/components"; import { SharedModule } from "../../../app/shared"; +import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "./login-with-device.component"; import { LoginComponent } from "./login.component"; @NgModule({ imports: [SharedModule, CheckboxModule], - declarations: [LoginComponent, LoginWithDeviceComponent], - exports: [LoginComponent, LoginWithDeviceComponent], + declarations: [LoginComponent, LoginWithDeviceComponent, LoginDecryptionOptionsComponent], + exports: [LoginComponent, LoginWithDeviceComponent, LoginDecryptionOptionsComponent], }) export class LoginModule {} diff --git a/apps/web/src/app/auth/recover-two-factor.component.ts b/apps/web/src/app/auth/recover-two-factor.component.ts index 06816d85460..2d6140780a0 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.ts @@ -35,7 +35,7 @@ export class RecoverTwoFactorComponent { request.recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase(); request.email = this.email.trim().toLowerCase(); const key = await this.authService.makePreloginKey(this.masterPassword, request.email); - request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key); + request.masterPasswordHash = await this.cryptoService.hashMasterKey(this.masterPassword, key); this.formPromise = this.apiService.postTwoFactorRecover(request); await this.formPromise; this.platformUtilsService.showToast( diff --git a/apps/web/src/app/settings/change-password.component.html b/apps/web/src/app/auth/settings/change-password.component.html similarity index 94% rename from apps/web/src/app/settings/change-password.component.html rename to apps/web/src/app/auth/settings/change-password.component.html index 3321b8f9dfe..7088cb5a686 100644 --- a/apps/web/src/app/settings/change-password.component.html +++ b/apps/web/src/app/auth/settings/change-password.component.html @@ -89,12 +89,12 @@ -
-
- -
- - + +
+ +
+ + +
+
+ + +
-
- - -
-
+
diff --git a/apps/web/src/app/settings/preferences.component.ts b/apps/web/src/app/settings/preferences.component.ts index 973aed0f5f7..9412a3e8b66 100644 --- a/apps/web/src/app/settings/preferences.component.ts +++ b/apps/web/src/app/settings/preferences.component.ts @@ -1,10 +1,10 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; -import { concatMap, filter, map, Observable, Subject, takeUntil, tap } from "rxjs"; +import { concatMap, filter, firstValueFrom, map, Observable, Subject, takeUntil, tap } from "rxjs"; import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { ThemeType } from "@bitwarden/common/enums"; @@ -24,6 +24,8 @@ export class PreferencesComponent implements OnInit { // For use in template protected readonly VaultTimeoutAction = VaultTimeoutAction; + protected availableVaultTimeoutActions$: Observable; + vaultTimeoutPolicyCallout: Observable<{ timeout: { hours: number; minutes: number }; action: VaultTimeoutAction; @@ -89,6 +91,9 @@ export class PreferencesComponent implements OnInit { } async ngOnInit() { + this.availableVaultTimeoutActions$ = + this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(); + this.vaultTimeoutPolicyCallout = this.policyService.get$(PolicyType.MaximumVaultTimeout).pipe( filter((policy) => policy != null), map((policy) => { @@ -133,7 +138,9 @@ export class PreferencesComponent implements OnInit { .subscribe(); const initialFormValues = { vaultTimeout: await this.vaultTimeoutSettingsService.getVaultTimeout(), - vaultTimeoutAction: await this.vaultTimeoutSettingsService.getVaultTimeoutAction(), + vaultTimeoutAction: await firstValueFrom( + this.vaultTimeoutSettingsService.vaultTimeoutAction$() + ), enableFavicons: !(await this.settingsService.getDisableFavicon()), enableFullWidth: await this.stateService.getEnableFullWidth(), theme: await this.stateService.getTheme(), diff --git a/apps/web/src/app/settings/security-keys.component.ts b/apps/web/src/app/settings/security-keys.component.ts index 887ba3d5879..2a01f6d0106 100644 --- a/apps/web/src/app/settings/security-keys.component.ts +++ b/apps/web/src/app/settings/security-keys.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ApiKeyComponent } from "./api-key.component"; @@ -20,14 +20,14 @@ export class SecurityKeysComponent implements OnInit { showChangeKdf = true; constructor( - private keyConnectorService: KeyConnectorService, + private userVerificationService: UserVerificationService, private stateService: StateService, private modalService: ModalService, private apiService: ApiService ) {} async ngOnInit() { - this.showChangeKdf = !(await this.keyConnectorService.getUsesKeyConnector()); + this.showChangeKdf = await this.userVerificationService.hasMasterPassword(); } async viewUserApiKey() { diff --git a/apps/web/src/app/settings/security-routing.module.ts b/apps/web/src/app/settings/security-routing.module.ts index 1d47a0d7750..d08d4be16fb 100644 --- a/apps/web/src/app/settings/security-routing.module.ts +++ b/apps/web/src/app/settings/security-routing.module.ts @@ -1,9 +1,9 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; +import { ChangePasswordComponent } from "../auth/settings/change-password.component"; import { TwoFactorSetupComponent } from "../auth/settings/two-factor-setup.component"; -import { ChangePasswordComponent } from "./change-password.component"; import { SecurityKeysComponent } from "./security-keys.component"; import { SecurityComponent } from "./security.component"; diff --git a/apps/web/src/app/settings/security.component.ts b/apps/web/src/app/settings/security.component.ts index 1c70f85edaf..3237a2e6a28 100644 --- a/apps/web/src/app/settings/security.component.ts +++ b/apps/web/src/app/settings/security.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @Component({ selector: "app-security", @@ -9,9 +9,9 @@ import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-con export class SecurityComponent { showChangePassword = true; - constructor(private keyConnectorService: KeyConnectorService) {} + constructor(private userVerificationService: UserVerificationService) {} async ngOnInit() { - this.showChangePassword = !(await this.keyConnectorService.getUsesKeyConnector()); + this.showChangePassword = await this.userVerificationService.hasMasterPassword(); } } diff --git a/apps/web/src/app/settings/update-key.component.html b/apps/web/src/app/settings/update-key.component.html index b39a5eb7e1a..7b94a6dca04 100644 --- a/apps/web/src/app/settings/update-key.component.html +++ b/apps/web/src/app/settings/update-key.component.html @@ -1,4 +1,4 @@ - From c6972f3e1c0fdf4a3fe1e7e52e6071ad37db7e10 Mon Sep 17 00:00:00 2001 From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:03:01 -0400 Subject: [PATCH 064/135] [SM-877] Updating trash to have ability to copy UUID (#5986) * [SM-877] Updating trash to have ability to copy UUID * Fixing the ordering of Div's for trash secret UUID copying * moving ngif to div * merge fixs --- .../shared/secrets-list.component.html | 11 +++-------- .../app/secrets-manager/trash/trash.component.html | 1 + .../src/app/secrets-manager/trash/trash.component.ts | 9 +++++++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html index e065141e828..a6660b3d3ac 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html @@ -65,16 +65,12 @@
-
-
+
{{ secret.name }}
{{ secret.id }}
-
{{ secret.name }}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.html index e4990f0111a..03dc17b98d5 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.html @@ -9,4 +9,5 @@ (restoreSecretsEvent)="openRestoreSecret($event)" [secrets]="secrets$ | async" [trash]="true" + (copySecretUuidEvent)="copySecretUuid($event)" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts index 83f510ed569..e92a01ed279 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts @@ -2,10 +2,13 @@ import { Component, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { combineLatestWith, Observable, startWith, switchMap } from "rxjs"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService } from "@bitwarden/components"; import { SecretListView } from "../models/view/secret-list.view"; import { SecretService } from "../secrets/secret.service"; +import { SecretsListComponent } from "../shared/secrets-list.component"; import { SecretHardDeleteDialogComponent, @@ -28,6 +31,8 @@ export class TrashComponent implements OnInit { constructor( private route: ActivatedRoute, private secretService: SecretService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, private dialogService: DialogService ) {} @@ -65,4 +70,8 @@ export class TrashComponent implements OnInit { }, }); } + + copySecretUuid(id: string) { + SecretsListComponent.copySecretUuid(id, this.platformUtilsService, this.i18nService); + } } From 17a4edcda19c60322066a13567057e3c98298a47 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:10:35 -0500 Subject: [PATCH 065/135] increase secret value max length limit (#6030) --- .../secrets-manager/secrets/dialog/secret-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts index c6a807f7395..426542823f9 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts @@ -40,7 +40,7 @@ export class SecretDialogComponent implements OnInit { validators: [Validators.required, Validators.maxLength(500), BitValidators.trimValidator], updateOn: "submit", }), - value: new FormControl("", [Validators.required, Validators.maxLength(3500)]), + value: new FormControl("", [Validators.required, Validators.maxLength(25000)]), notes: new FormControl("", { validators: [Validators.maxLength(7000), BitValidators.trimValidator], updateOn: "submit", From b5c4149bc8f12ca5c07d38858943f07fb01a0221 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Mon, 28 Aug 2023 15:31:09 -0400 Subject: [PATCH 066/135] CLI/Release Desktop Workflow - Update runner to Ubuntu latest (#6129) --- .github/workflows/build-cli.yml | 8 ++++---- .github/workflows/release-desktop.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index da076303508..4d19c7e7cce 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -35,7 +35,7 @@ defaults: jobs: cloc: name: CLOC - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repo uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 @@ -51,7 +51,7 @@ jobs: setup: name: Setup - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: package_version: ${{ steps.retrieve-version.outputs.package_version }} steps: @@ -69,7 +69,7 @@ jobs: name: Build CLI ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, macos-11] + os: [ubuntu-22.04, macos-11] runs-on: ${{ matrix.os }} needs: - setup @@ -368,7 +368,7 @@ jobs: check-failures: name: Check for failures if: always() - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - cloc - setup diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index c564c1c7254..97c91776520 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -47,7 +47,7 @@ defaults: jobs: setup: name: Setup - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: release-version: ${{ steps.version.outputs.version }} release-channel: ${{ steps.release-channel.outputs.channel }} @@ -247,7 +247,7 @@ jobs: snap: name: Deploy Snap - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: setup if: inputs.snap_publish env: From 3cc0e5f59a7004cd25d9ed19ce92ca1e33b91975 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Mon, 28 Aug 2023 16:12:40 -0400 Subject: [PATCH 067/135] Fix Release CLI workflow (#6130) --- .github/workflows/release-cli.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index eaaeafd62c8..9ff812bf305 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -182,6 +182,8 @@ jobs: - name: Publish Snap & logout if: ${{ github.event.inputs.release_type != 'Dry Run' }} + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} run: | snapcraft push bw_${{ env._PKG_VERSION }}_amd64.snap --release stable snapcraft logout From fd119f08ec667841cdde66a74518dffa157340b1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 16:35:34 -0400 Subject: [PATCH 068/135] Bumped desktop version to 2023.8.3 (#6131) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/package.json | 2 +- apps/desktop/src/package-lock.json | 4 ++-- apps/desktop/src/package.json | 2 +- package-lock.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 83a3aedde21..82c77156c12 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2023.8.2", + "version": "2023.8.3", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index f91ee186705..54af2baea1e 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2023.8.2", + "version": "2023.8.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2023.8.2", + "version": "2023.8.3", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-native": "file:../desktop_native" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 2853ab64649..b171046bcef 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2023.8.2", + "version": "2023.8.3", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/package-lock.json b/package-lock.json index 2c955a3ec02..0b364d58ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -231,7 +231,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2023.8.2", + "version": "2023.8.3", "hasInstallScript": true, "license": "GPL-3.0" }, From 42193aecb83c11eaddad7c25c5739f964febb255 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Tue, 29 Aug 2023 09:10:16 -0400 Subject: [PATCH 069/135] [PM-1407] Improve iframe sandbox detection (#5976) * improve iframe sandbox detection * code cleanup Co-authored-by: Cesar Gonzalez * update autofill v1 logic as well --------- Co-authored-by: Cesar Gonzalez --- apps/browser/src/autofill/content/autofill.js | 12 ++++++++++-- apps/browser/src/autofill/content/autofillv2.ts | 10 +++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/autofill/content/autofill.js b/apps/browser/src/autofill/content/autofill.js index f6db33af97d..1833c09e159 100644 --- a/apps/browser/src/autofill/content/autofill.js +++ b/apps/browser/src/autofill/content/autofill.js @@ -768,8 +768,16 @@ // Detect if within an iframe, and the iframe is sandboxed function isSandboxed() { - // self.origin is 'null' if inside a frame with sandboxed csp or iframe tag - return self.origin == null || self.origin === 'null'; + // self.origin is 'null' if inside a frame with sandboxed csp or iframe tag + if (String(self.origin).toLowerCase() === "null") { + return true; + } + + if (window.frameElement?.hasAttribute("sandbox")) { + return true; + } + + return location.hostname === ""; } function doFill(fillScript) { diff --git a/apps/browser/src/autofill/content/autofillv2.ts b/apps/browser/src/autofill/content/autofillv2.ts index 8bf16ff879c..65813b3afe6 100644 --- a/apps/browser/src/autofill/content/autofillv2.ts +++ b/apps/browser/src/autofill/content/autofillv2.ts @@ -849,7 +849,15 @@ function fill(document: Document, fillScript: AutofillScript) { // Detect if within an iframe, and the iframe is sandboxed function isSandboxed() { // self.origin is 'null' if inside a frame with sandboxed csp or iframe tag - return self.origin == null || self.origin === "null"; + if (String(self.origin).toLowerCase() === "null") { + return true; + } + + if (window.frameElement?.hasAttribute("sandbox")) { + return true; + } + + return location.hostname === ""; } function doFill(fillScript: AutofillScript) { From 6bf559b9321201ba78692e13ffed00e8ed7b79fb Mon Sep 17 00:00:00 2001 From: Opeyemi Date: Tue, 29 Aug 2023 16:00:28 +0100 Subject: [PATCH 070/135] UPDATE: fix all warnings in Clients workflow (#6137) --- .github/workflows/brew-bump-cli.yml | 2 +- .github/workflows/brew-bump-desktop.yml | 2 +- .github/workflows/release-desktop.yml | 2 +- .github/workflows/version-auto-bump.yml | 2 -- .github/workflows/version-bump.yml | 3 --- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/brew-bump-cli.yml b/.github/workflows/brew-bump-cli.yml index 5bee549032b..477c9ace582 100644 --- a/.github/workflows/brew-bump-cli.yml +++ b/.github/workflows/brew-bump-cli.yml @@ -38,4 +38,4 @@ jobs: formula: bitwarden-cli tag: ${{ github.ref }} revision: ${{ github.sha }} - force: false + force: true diff --git a/.github/workflows/brew-bump-desktop.yml b/.github/workflows/brew-bump-desktop.yml index f8bf833dcfa..0a5c3947161 100644 --- a/.github/workflows/brew-bump-desktop.yml +++ b/.github/workflows/brew-bump-desktop.yml @@ -38,5 +38,5 @@ jobs: cask: bitwarden tag: ${{ github.ref }} revision: ${{ github.sha }} - force: false + force: true dryrun: true diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 97c91776520..99119725a3e 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -185,7 +185,7 @@ jobs: --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com - name: Get checksum files - uses: bitwarden/gh-actions/get-checksum@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-checksum@82cfceb235b308c2eb63923824e61d8350d280db with: packages_dir: "apps/desktop/artifacts" file_path: "apps/desktop/artifacts/sha256-checksums.txt" diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 2c13ec05b38..857099db511 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -42,8 +42,6 @@ jobs: name: Bump version to ${{ needs.setup.outputs.version_number }} needs: setup uses: ./.github/workflows/version-bump.yml - secrets: - AZURE_PROD_KV_CREDENTIALS: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} with: version_number: ${{ needs.setup.outputs.version_number }} client: "Desktop" diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 6ea1198784a..420ef456ec0 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -26,9 +26,6 @@ on: client: required: true type: string - secrets: - AZURE_PROD_KV_CREDENTIALS: - required: true defaults: run: From c8c314dd35b8e832bab9e36a79ca6f2e411ab5eb Mon Sep 17 00:00:00 2001 From: Danielle Flinn <43477473+danielleflinn@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:42:56 -0700 Subject: [PATCH 071/135] [PM-2866] - Update color variables for better contrast (#6078) * Update variables.scss * update toast text color to have better WCAG contrast * added toastcolor variables * Update window.main.ts * Tweaked styles * darkened backgroundAlt2 and button background * lightened button border * lightened button backgroundColor * Update window.main.ts * updated brand colors and added toastTextColor variable * lightened solarize danger variable to meet WCAG contrast with dark text * updated browser solarize variable to match tw-theme.css --- apps/browser/src/popup/scss/variables.scss | 10 +++++----- apps/desktop/src/scss/variables.scss | 8 ++++---- apps/web/src/scss/variables.scss | 6 +++--- libs/components/src/tw-theme.css | 2 +- libs/components/src/variables.scss | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/browser/src/popup/scss/variables.scss b/apps/browser/src/popup/scss/variables.scss index 05843a9b351..2cdc49cd9ef 100644 --- a/apps/browser/src/popup/scss/variables.scss +++ b/apps/browser/src/popup/scss/variables.scss @@ -24,10 +24,10 @@ $gray-light: #777; $text-muted: $gray-light; $brand-primary: #175ddc; -$brand-danger: #dd4b39; -$brand-success: #00a65a; +$brand-danger: #c83522; +$brand-success: #017e45; $brand-info: #555555; -$brand-warning: #bf7e16; +$brand-warning: #8b6609; $brand-primary-accent: #1252a3; $background-color: #f0f0f0; @@ -237,7 +237,7 @@ $themes: ( passwordCountText: $nord5, calloutBorderColor: $nord0, calloutBackgroundColor: $nord2, - toastTextColor: #ffffff, + toastTextColor: #000000, svgSuffix: "-dark.svg", transparentColor: rgba(0, 0, 0, 0), dateInputColorScheme: dark, @@ -299,7 +299,7 @@ $themes: ( passwordCountText: $solarizedDarkBase2, calloutBorderColor: $solarizedDarkBase03, calloutBackgroundColor: $solarizedDarkBase01, - toastTextColor: #ffffff, + toastTextColor: #000000, svgSuffix: "-solarized.svg", transparentColor: rgba(0, 0, 0, 0), dateInputColorScheme: dark, diff --git a/apps/desktop/src/scss/variables.scss b/apps/desktop/src/scss/variables.scss index b99881134d5..3ad4c0f0754 100644 --- a/apps/desktop/src/scss/variables.scss +++ b/apps/desktop/src/scss/variables.scss @@ -20,10 +20,10 @@ $gray-light: #777; $text-muted: $gray-light; $brand-primary: #175ddc; -$brand-danger: #dd4b39; -$brand-success: #00a65a; +$brand-danger: #c83522; +$brand-success: #017e45; $brand-info: #555555; -$brand-warning: #bf7e16; +$brand-warning: #8b6609; $brand-primary-accent: #1252a3; $background-color: white; @@ -211,7 +211,7 @@ $themes: ( passwordCountText: $nord5, calloutBorderColor: $nord1, calloutBackgroundColor: $nord2, - toastTextColor: #ffffff, + toastTextColor: #000000, accountSwitcherBackgroundColor: $nord0, accountSwitcherTextColor: $nord5, svgSuffix: "-dark.svg", diff --git a/apps/web/src/scss/variables.scss b/apps/web/src/scss/variables.scss index 222c43fed19..719f403e385 100644 --- a/apps/web/src/scss/variables.scss +++ b/apps/web/src/scss/variables.scss @@ -4,10 +4,10 @@ $primary: #175ddc; $primary-accent: #1252a3; $secondary: #ced4da; $secondary-alt: #1a3b66; -$success: #00a65a; +$success: #017e45; $info: #555555; -$warning: #bf7e16; -$danger: #dd4b39; +$warning: #8b6609; +$danger: #c83522; $white: #ffffff; // Bootstrap Variable Overrides diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css index 420b2cfd483..1ff2064fdbd 100644 --- a/libs/components/src/tw-theme.css +++ b/libs/components/src/tw-theme.css @@ -151,7 +151,7 @@ --color-text-main: 253 246 227; --color-text-muted: 147 161 161; - --color-text-contrast: 0 43 54; + --color-text-contrast: 0 0 0; --color-text-alt2: 255 255 255; --color-text-code: 240 141 199; diff --git a/libs/components/src/variables.scss b/libs/components/src/variables.scss index 1abfd645fd2..88e3cba5c3c 100644 --- a/libs/components/src/variables.scss +++ b/libs/components/src/variables.scss @@ -4,10 +4,10 @@ $primary: #175ddc; $primary-accent: #1252a3; $secondary: #ced4da; $secondary-alt: #1a3b66; -$success: #00a65a; +$success: #017e45; $info: #555555; -$warning: #bf7e16; -$danger: #dd4b39; +$warning: #8b6609; +$danger: #c83522; $white: #ffffff; // Bootstrap Variable Overrides @@ -85,7 +85,7 @@ $mfaTypes: 0, 2, 3, 4, 6; // Theme Variables // Light -$lightDangerHover: #c43421; +$lightDangerHover: #c83522; $lightInputColor: #465057; $lightInputPlaceholderColor: #b6b8b8; From 3930189c4e90408eff4a2f60499e842e5011c982 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:08:02 -0400 Subject: [PATCH 072/135] PM-3632 - Vault Timeout Input comp (all clients) - resolve bug where logic which was supposed to initialize the custom vault timeout timeframe if coming from a previously selected numeric timeout was not working properly. (#6102) --- .../components/settings/vault-timeout-input.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/angular/src/components/settings/vault-timeout-input.component.ts b/libs/angular/src/components/settings/vault-timeout-input.component.ts index b0dec5affb7..e01a01fdd1d 100644 --- a/libs/angular/src/components/settings/vault-timeout-input.component.ts +++ b/libs/angular/src/components/settings/vault-timeout-input.component.ts @@ -76,14 +76,17 @@ export class VaultTimeoutInputComponent } }); - // Assign the previous value to the custom fields + // Assign the current value to the custom fields + // so that if the user goes from a numeric value to custom + // we can initialize the custom fields with the current value + // ex: user picks 5 min, goes to custom, we want to show 0 hr, 5 min in the custom fields this.form.controls.vaultTimeout.valueChanges .pipe( filter((value) => value !== VaultTimeoutInputComponent.CUSTOM_VALUE), takeUntil(this.destroy$) ) - .subscribe((_) => { - const current = Math.max(this.form.value.vaultTimeout, 0); + .subscribe((value) => { + const current = Math.max(value, 0); this.form.patchValue({ custom: { hours: Math.floor(current / 60), From 9288367bc8925581c0620c94be9ccc94eee220cb Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:46:10 -0400 Subject: [PATCH 073/135] Fix logic in workflow (#6147) --- .github/workflows/release-desktop.yml | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 99119725a3e..22b76f46408 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -56,7 +56,7 @@ jobs: uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Branch check - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} run: | if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-desktop" ]]; then echo "===================================" @@ -69,7 +69,7 @@ jobs: id: version uses: bitwarden/gh-actions/release-version-check@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: - release-type: ${{ github.event.inputs.release_type }} + release-type: ${{ inputs.release_type }} project-type: ts file: apps/desktop/src/package.json monorepo: true @@ -93,7 +93,7 @@ jobs: esac - name: Create GitHub deployment - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: @@ -122,7 +122,7 @@ jobs: cf-prod-account" - name: Download all artifacts - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: workflow: build-desktop.yml @@ -131,7 +131,7 @@ jobs: path: apps/desktop/artifacts - name: Dry Run - Download all artifacts - if: ${{ github.event.inputs.release_type == 'Dry Run' }} + if: ${{ inputs.release_type == 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: workflow: build-desktop.yml @@ -146,17 +146,17 @@ jobs: run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive - name: Set staged rollout percentage - if: ${{ github.event.inputs.electron_publish }} + if: ${{ inputs.electron_publish == 'true' }} env: RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }} - ROLLOUT_PCT: ${{ github.event.inputs.rollout_percentage }} + ROLLOUT_PCT: ${{ inputs.rollout_percentage }} run: | echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}.yml echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-linux.yml echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-mac.yml - name: Publish artifacts to S3 - if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish }} + if: ${{ inputs.release_type != 'Dry Run' && inputs.electron_publish == 'true' }} env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }} @@ -170,7 +170,7 @@ jobs: --quiet - name: Publish artifacts to R2 - if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish }} + if: ${{ inputs.release_type != 'Dry Run' && inputs.electron_publish == 'true' }} env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }} @@ -192,7 +192,7 @@ jobs: - name: Create Release uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 - if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' && inputs.github_release }} + if: ${{ steps.release-channel.outputs.channel == 'latest' && inputs.release_type != 'Dry Run' && inputs.github_release == 'true' }} env: PKG_VERSION: ${{ steps.version.outputs.version }} RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }} @@ -230,7 +230,7 @@ jobs: draft: true - name: Update deployment status to Success - if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} + if: ${{ inputs.release_type != 'Dry Run' && success() }} uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -238,7 +238,7 @@ jobs: deployment-id: ${{ steps.deployment.outputs.deployment_id }} - name: Update deployment status to Failure - if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} + if: ${{ inputs.release_type != 'Dry Run' && failure() }} uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -249,7 +249,7 @@ jobs: name: Deploy Snap runs-on: ubuntu-22.04 needs: setup - if: inputs.snap_publish + if: ${{ inputs.snap_publish == 'true' }} env: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: @@ -278,7 +278,7 @@ jobs: working-directory: apps/desktop - name: Download Snap artifact - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: workflow: build-desktop.yml @@ -288,7 +288,7 @@ jobs: path: apps/desktop/dist - name: Dry Run - Download Snap artifact - if: ${{ github.event.inputs.release_type == 'Dry Run' }} + if: ${{ inputs.release_type == 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: workflow: build-desktop.yml @@ -298,7 +298,7 @@ jobs: path: apps/desktop/dist - name: Deploy to Snap Store - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} env: SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} run: | @@ -310,7 +310,7 @@ jobs: name: Deploy Choco runs-on: windows-2019 needs: setup - if: inputs.choco_publish + if: ${{ inputs.choco_publish == 'true' }} env: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: @@ -346,7 +346,7 @@ jobs: working-directory: apps/desktop - name: Download choco artifact - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: workflow: build-desktop.yml @@ -356,7 +356,7 @@ jobs: path: apps/desktop/dist - name: Dry Run - Download choco artifact - if: ${{ github.event.inputs.release_type == 'Dry Run' }} + if: ${{ inputs.release_type == 'Dry Run' }} uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: workflow: build-desktop.yml @@ -366,7 +366,7 @@ jobs: path: apps/desktop/dist - name: Push to Chocolatey - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} shell: pwsh run: choco push --source=https://push.chocolatey.org/ working-directory: apps/desktop/dist From b444eed0b5b61fb66eb89d1c90ee0ded89a3f6d9 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Wed, 30 Aug 2023 10:18:20 -0500 Subject: [PATCH 074/135] [PM-3589] Context Menu No Longer Shows Autofill Ciphers (#6085) * [PM-3589] Context Menu No Longer Shows Autofill Ciphers * [PM-3589] Ensuring that passwordless users can also access ciphers that require reprompt * [PM-3589] Fixing jest test * [PM-3589] Fixing issue where context menu autofill does not allow filling when passwordless setup is in place --- .../cipher-context-menu-handler.spec.ts | 57 +------------------ .../browser/cipher-context-menu-handler.ts | 14 +---- .../context-menu-clicked-handler.spec.ts | 5 +- .../browser/context-menu-clicked-handler.ts | 21 +++++-- .../browser/src/background/main.background.ts | 6 +- .../popup/components/vault/view.component.ts | 10 +--- 6 files changed, 32 insertions(+), 81 deletions(-) diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts index dbe391ce4ab..d7cac8d44b2 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts @@ -1,7 +1,6 @@ import { mock, MockProxy } from "jest-mock-extended"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; @@ -14,7 +13,6 @@ describe("CipherContextMenuHandler", () => { let mainContextMenuHandler: MockProxy; let authService: MockProxy; let cipherService: MockProxy; - let userVerificationService: MockProxy; let sut: CipherContextMenuHandler; @@ -22,17 +20,10 @@ describe("CipherContextMenuHandler", () => { mainContextMenuHandler = mock(); authService = mock(); cipherService = mock(); - userVerificationService = mock(); - userVerificationService.hasMasterPassword.mockResolvedValue(true); jest.spyOn(MainContextMenuHandler, "removeAll").mockResolvedValue(); - sut = new CipherContextMenuHandler( - mainContextMenuHandler, - authService, - cipherService, - userVerificationService - ); + sut = new CipherContextMenuHandler(mainContextMenuHandler, authService, cipherService); }); afterEach(() => jest.resetAllMocks()); @@ -78,7 +69,7 @@ describe("CipherContextMenuHandler", () => { expect(mainContextMenuHandler.noLogins).toHaveBeenCalledTimes(1); }); - it("only adds valid ciphers", async () => { + it("only adds login ciphers including ciphers that require reprompt", async () => { authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); mainContextMenuHandler.init.mockResolvedValue(true); @@ -90,47 +81,6 @@ describe("CipherContextMenuHandler", () => { name: "Test Cipher", login: { username: "Test Username" }, }; - - cipherService.getAllDecryptedForUrl.mockResolvedValue([ - null, // invalid cipher - undefined, // invalid cipher - { type: CipherType.Card }, // invalid cipher - { type: CipherType.Login, reprompt: CipherRepromptType.Password }, // invalid cipher - realCipher, // valid cipher - ] as any[]); - - await sut.update("https://test.com"); - - expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledTimes(1); - - expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); - - expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(2); - - expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( - "Test Cipher (Test Username)", - "5", - "https://test.com", - realCipher - ); - }); - - it("adds ciphers with master password reprompt if the user does not have a master password", async () => { - authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); - - // User does not have a master password, or has one but hasn't logged in with it (key connector user or TDE user) - userVerificationService.hasMasterPasswordAndMasterKeyHash.mockResolvedValue(false); - - mainContextMenuHandler.init.mockResolvedValue(true); - - const realCipher = { - id: "5", - type: CipherType.Login, - reprompt: CipherRepromptType.None, - name: "Test Cipher", - login: { username: "Test Username" }, - }; - const repromptCipher = { id: "6", type: CipherType.Login, @@ -143,8 +93,8 @@ describe("CipherContextMenuHandler", () => { null, // invalid cipher undefined, // invalid cipher { type: CipherType.Card }, // invalid cipher - repromptCipher, // valid cipher realCipher, // valid cipher + repromptCipher, ] as any[]); await sut.update("https://test.com"); @@ -153,7 +103,6 @@ describe("CipherContextMenuHandler", () => { expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); - // Should call this twice, once for each valid cipher expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(2); expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index 1d1be8f8386..fe6479aae51 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -1,5 +1,4 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -12,7 +11,6 @@ import { authServiceFactory, AuthServiceInitOptions, } from "../../auth/background/service-factories/auth-service.factory"; -import { userVerificationServiceFactory } from "../../auth/background/service-factories/user-verification-service.factory"; import { Account } from "../../models/account"; import { CachedServices } from "../../platform/background/service-factories/factory-options"; import { BrowserApi } from "../../platform/browser/browser-api"; @@ -39,8 +37,7 @@ export class CipherContextMenuHandler { constructor( private mainContextMenuHandler: MainContextMenuHandler, private authService: AuthService, - private cipherService: CipherService, - private userVerificationService: UserVerificationService + private cipherService: CipherService ) {} static async create(cachedServices: CachedServices) { @@ -79,8 +76,7 @@ export class CipherContextMenuHandler { return new CipherContextMenuHandler( await MainContextMenuHandler.mv3Create(cachedServices), await authServiceFactory(cachedServices, serviceOptions), - await cipherServiceFactory(cachedServices, serviceOptions), - await userVerificationServiceFactory(cachedServices, serviceOptions) + await cipherServiceFactory(cachedServices, serviceOptions) ); } @@ -180,11 +176,7 @@ export class CipherContextMenuHandler { } private async updateForCipher(url: string, cipher: CipherView) { - if ( - cipher == null || - cipher.type !== CipherType.Login || - (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) - ) { + if (cipher == null || cipher.type !== CipherType.Login) { return; } diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts index a9dbcbaacc5..021d15df89e 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts @@ -3,6 +3,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; @@ -63,6 +64,7 @@ describe("ContextMenuClickedHandler", () => { let cipherService: MockProxy; let totpService: MockProxy; let eventCollectionService: MockProxy; + let userVerificationService: MockProxy; let sut: ContextMenuClickedHandler; @@ -82,7 +84,8 @@ describe("ContextMenuClickedHandler", () => { authService, cipherService, totpService, - eventCollectionService + eventCollectionService, + userVerificationService ); }); diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts index 38e605abe70..9a14ea06da0 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts @@ -1,6 +1,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { EventType } from "@bitwarden/common/enums"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; @@ -14,6 +15,7 @@ import { AuthServiceInitOptions, } from "../../auth/background/service-factories/auth-service.factory"; import { totpServiceFactory } from "../../auth/background/service-factories/totp-service.factory"; +import { userVerificationServiceFactory } from "../../auth/background/service-factories/user-verification-service.factory"; import LockedVaultPendingNotificationsItem from "../../background/models/lockedVaultPendingNotificationsItem"; import { eventCollectionServiceFactory } from "../../background/service-factories/event-collection-service.factory"; import { Account } from "../../models/account"; @@ -56,7 +58,8 @@ export class ContextMenuClickedHandler { private authService: AuthService, private cipherService: CipherService, private totpService: TotpService, - private eventCollectionService: EventCollectionService + private eventCollectionService: EventCollectionService, + private userVerificationService: UserVerificationService ) {} static async mv3Create(cachedServices: CachedServices) { @@ -109,7 +112,8 @@ export class ContextMenuClickedHandler { await authServiceFactory(cachedServices, serviceOptions), await cipherServiceFactory(cachedServices, serviceOptions), await totpServiceFactory(cachedServices, serviceOptions), - await eventCollectionServiceFactory(cachedServices, serviceOptions) + await eventCollectionServiceFactory(cachedServices, serviceOptions), + await userVerificationServiceFactory(cachedServices, serviceOptions) ); } @@ -204,7 +208,7 @@ export class ContextMenuClickedHandler { return; } - if (cipher.reprompt !== CipherRepromptType.None) { + if (await this.isPasswordRepromptRequired(cipher)) { await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { cipherId: cipher.id, action: AUTOFILL_ID, @@ -218,7 +222,7 @@ export class ContextMenuClickedHandler { this.copyToClipboard({ text: cipher.login.username, tab: tab }); break; case COPY_PASSWORD_ID: - if (cipher.reprompt !== CipherRepromptType.None) { + if (await this.isPasswordRepromptRequired(cipher)) { await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { cipherId: cipher.id, action: COPY_PASSWORD_ID, @@ -230,7 +234,7 @@ export class ContextMenuClickedHandler { break; case COPY_VERIFICATIONCODE_ID: - if (cipher.reprompt !== CipherRepromptType.None) { + if (await this.isPasswordRepromptRequired(cipher)) { await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { cipherId: cipher.id, action: COPY_VERIFICATIONCODE_ID, @@ -246,6 +250,13 @@ export class ContextMenuClickedHandler { } } + private async isPasswordRepromptRequired(cipher: CipherView): Promise { + return ( + cipher.reprompt === CipherRepromptType.Password && + (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) + ); + } + private async getIdentifier(tab: chrome.tabs.Tab, info: chrome.contextMenus.OnClickData) { return new Promise((resolve, reject) => { BrowserApi.sendTabsMessage( diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 617acc2bf78..c65a8697631 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -635,7 +635,8 @@ export default class MainBackground { this.authService, this.cipherService, this.totpService, - this.eventCollectionService + this.eventCollectionService, + this.userVerificationService ); this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler); @@ -670,8 +671,7 @@ export default class MainBackground { this.cipherContextMenuHandler = new CipherContextMenuHandler( this.mainContextMenuHandler, this.authService, - this.cipherService, - this.userVerificationService + this.cipherService ); } } diff --git a/apps/browser/src/vault/popup/components/vault/view.component.ts b/apps/browser/src/vault/popup/components/vault/view.component.ts index a70a11475ca..8f45547737a 100644 --- a/apps/browser/src/vault/popup/components/vault/view.component.ts +++ b/apps/browser/src/vault/popup/components/vault/view.component.ts @@ -170,8 +170,8 @@ export class ViewComponent extends BaseViewComponent { switch (this.loadAction) { case AUTOFILL_ID: - this.fillCipher(); - return; + await this.fillCipher(); + break; case COPY_USERNAME_ID: await this.copy(this.cipher.login.username, "username", "Username"); break; @@ -186,7 +186,7 @@ export class ViewComponent extends BaseViewComponent { } if (this.inPopout && this.loadAction) { - this.close(); + setTimeout(() => this.close(), 1000); } } @@ -238,10 +238,6 @@ export class ViewComponent extends BaseViewComponent { const didAutofill = await this.doAutofill(); if (didAutofill) { this.platformUtilsService.showToast("success", null, this.i18nService.t("autoFillSuccess")); - - if (this.inPopout) { - this.close(); - } } } From 3340af8084514ffb1edc300a2dae8d7af95798b1 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 30 Aug 2023 12:57:20 -0500 Subject: [PATCH 075/135] PM-3585 Improve state migrations (#5009) * WIP: safer state migrations Co-authored-by: Justin Baur * Add min version check and remove old migrations Co-authored-by: Oscar Hinton * Add rollback and version checking * Add state version move migration * Expand tests and improve typing for Migrations * Remove StateMigration Service * Rewrite version 5 and 6 migrations * Add all but initial migration to supported migrations * Handle stateVersion location in migrator update versions * Move to unique migrations directory * Disallow imports outside of state-migrations * Lint and test fixes * Do not run migrations if we cannot determine state * Fix desktop background StateService build * Document Migration builder class * Add debug logging to migrations * Comment on migrator overrides * Use specific property names * `npm run prettier` :robot: * Insert new migration * Set stateVersion when creating new globals object * PR comments * Fix migrate imports * Move migration building into `migrate` function * Export current version from migration definitions * Move file version concerns to migrator * Update migrate spec to reflect new version requirements * Fix import paths * Prefer unique state data * Remove unnecessary async * Prefer to not use `any` --------- Co-authored-by: Justin Baur Co-authored-by: Oscar Hinton --- .../browser/cipher-context-menu-handler.ts | 3 - .../browser/context-menu-clicked-handler.ts | 3 - .../browser/main-context-menu-handler.ts | 3 - .../browser/src/background/main.background.ts | 8 - .../state-migration-service.factory.ts | 40 -- .../state-service.factory.ts | 8 +- .../platform/listeners/on-command-listener.ts | 6 - .../platform/listeners/on-install-listener.ts | 3 - .../src/platform/listeners/update-badge.ts | 3 - .../services/browser-state.service.spec.ts | 4 - .../services/browser-state.service.ts | 3 - .../src/popup/services/services.module.ts | 18 +- apps/cli/src/bw.ts | 9 - .../src/app/services/services.module.ts | 2 - apps/desktop/src/main.ts | 1 - apps/web/src/app/core/core.module.ts | 7 - .../src/app/core/state-migration.service.ts | 13 - apps/web/src/app/core/state/state.service.ts | 3 - .../src/services/jslib-services.module.ts | 8 - libs/common/src/enums/index.ts | 1 - libs/common/src/enums/state-version.enum.ts | 10 - .../abstractions/state-migration.service.ts | 4 - .../platform/abstractions/state.service.ts | 2 - .../platform/models/domain/global-state.ts | 3 +- .../services/state-migration.service.spec.ts | 216 ------- .../services/state-migration.service.ts | 587 ------------------ .../src/platform/services/state.service.ts | 24 +- .../src/state-migrations/.eslintrc.json | 24 + libs/common/src/state-migrations/index.ts | 1 + .../src/state-migrations/migrate.spec.ts | 67 ++ libs/common/src/state-migrations/migrate.ts | 60 ++ .../migration-builder.spec.ts | 117 ++++ .../src/state-migrations/migration-builder.ts | 106 ++++ .../state-migrations/migration-helper.spec.ts | 84 +++ .../src/state-migrations/migration-helper.ts | 37 ++ .../migrations/3-fix-premium.spec.ts | 111 ++++ .../migrations/3-fix-premium.ts | 48 ++ .../4-remove-ever-been-unlocked.spec.ts | 75 +++ .../migrations/4-remove-ever-been-unlocked.ts | 32 + .../5-add-key-type-to-org-keys.spec.ts | 141 +++++ .../migrations/5-add-key-type-to-org-keys.ts | 67 ++ .../6-remove-legacy-etm-key.spec.ts | 80 +++ .../migrations/6-remove-legacy-etm-key.ts | 32 + ...e-biometric-auto-prompt-to-account.spec.ts | 102 +++ ...7-move-biometric-auto-prompt-to-account.ts | 45 ++ .../migrations/8-move-state-version.spec.ts | 90 +++ .../migrations/8-move-state-version.ts | 37 ++ .../migrations/min-version.spec.ts | 29 + .../migrations/min-version.ts | 26 + .../src/state-migrations/migrator.spec.ts | 75 +++ libs/common/src/state-migrations/migrator.ts | 40 ++ 51 files changed, 1538 insertions(+), 980 deletions(-) delete mode 100644 apps/browser/src/platform/background/service-factories/state-migration-service.factory.ts delete mode 100644 apps/web/src/app/core/state-migration.service.ts delete mode 100644 libs/common/src/enums/state-version.enum.ts delete mode 100644 libs/common/src/platform/abstractions/state-migration.service.ts delete mode 100644 libs/common/src/platform/services/state-migration.service.spec.ts delete mode 100644 libs/common/src/platform/services/state-migration.service.ts create mode 100644 libs/common/src/state-migrations/.eslintrc.json create mode 100644 libs/common/src/state-migrations/index.ts create mode 100644 libs/common/src/state-migrations/migrate.spec.ts create mode 100644 libs/common/src/state-migrations/migrate.ts create mode 100644 libs/common/src/state-migrations/migration-builder.spec.ts create mode 100644 libs/common/src/state-migrations/migration-builder.ts create mode 100644 libs/common/src/state-migrations/migration-helper.spec.ts create mode 100644 libs/common/src/state-migrations/migration-helper.ts create mode 100644 libs/common/src/state-migrations/migrations/3-fix-premium.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/3-fix-premium.ts create mode 100644 libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.ts create mode 100644 libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.ts create mode 100644 libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.ts create mode 100644 libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.ts create mode 100644 libs/common/src/state-migrations/migrations/8-move-state-version.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/8-move-state-version.ts create mode 100644 libs/common/src/state-migrations/migrations/min-version.spec.ts create mode 100644 libs/common/src/state-migrations/migrations/min-version.ts create mode 100644 libs/common/src/state-migrations/migrator.spec.ts create mode 100644 libs/common/src/state-migrations/migrator.ts diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index fe6479aae51..6140db260f5 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -66,9 +66,6 @@ export class CipherContextMenuHandler { clipboardWriteCallback: NOT_IMPLEMENTED, win: self, }, - stateMigrationServiceOptions: { - stateFactory: stateFactory, - }, stateServiceOptions: { stateFactory: stateFactory, }, diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts index 9a14ea06da0..a6bff50a195 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts @@ -88,9 +88,6 @@ export class ContextMenuClickedHandler { clipboardWriteCallback: NOT_IMPLEMENTED, win: self, }, - stateMigrationServiceOptions: { - stateFactory: stateFactory, - }, stateServiceOptions: { stateFactory: stateFactory, }, diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.ts index 9b16aa266db..b9af3dd191f 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.ts @@ -79,9 +79,6 @@ export class MainContextMenuHandler { logServiceOptions: { isDev: false, }, - stateMigrationServiceOptions: { - stateFactory: stateFactory, - }, stateServiceOptions: { stateFactory: stateFactory, }, diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index c65a8697631..31e81c198f6 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -59,7 +59,6 @@ import { EncryptServiceImplementation } from "@bitwarden/common/platform/service import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; -import { StateMigrationService } from "@bitwarden/common/platform/services/state-migration.service"; import { SystemService } from "@bitwarden/common/platform/services/system.service"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service"; @@ -177,7 +176,6 @@ export default class MainBackground { searchService: SearchServiceAbstraction; notificationsService: NotificationsServiceAbstraction; stateService: StateServiceAbstraction; - stateMigrationService: StateMigrationService; systemService: SystemServiceAbstraction; eventCollectionService: EventCollectionServiceAbstraction; eventUploadService: EventUploadServiceAbstraction; @@ -262,17 +260,11 @@ export default class MainBackground { new KeyGenerationService(this.cryptoFunctionService) ) : new MemoryStorageService(); - this.stateMigrationService = new StateMigrationService( - this.storageService, - this.secureStorageService, - new StateFactory(GlobalState, Account) - ); this.stateService = new BrowserStateService( this.storageService, this.secureStorageService, this.memoryStorageService, this.logService, - this.stateMigrationService, new StateFactory(GlobalState, Account) ); this.platformUtilsService = new BrowserPlatformUtilsService( diff --git a/apps/browser/src/platform/background/service-factories/state-migration-service.factory.ts b/apps/browser/src/platform/background/service-factories/state-migration-service.factory.ts deleted file mode 100644 index 8d4ee969583..00000000000 --- a/apps/browser/src/platform/background/service-factories/state-migration-service.factory.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; -import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; -import { StateMigrationService } from "@bitwarden/common/platform/services/state-migration.service"; - -import { Account } from "../../../models/account"; - -import { CachedServices, factory, FactoryOptions } from "./factory-options"; -import { - diskStorageServiceFactory, - DiskStorageServiceInitOptions, - secureStorageServiceFactory, - SecureStorageServiceInitOptions, -} from "./storage-service.factory"; - -type StateMigrationServiceFactoryOptions = FactoryOptions & { - stateMigrationServiceOptions: { - stateFactory: StateFactory; - }; -}; - -export type StateMigrationServiceInitOptions = StateMigrationServiceFactoryOptions & - DiskStorageServiceInitOptions & - SecureStorageServiceInitOptions; - -export function stateMigrationServiceFactory( - cache: { stateMigrationService?: StateMigrationService } & CachedServices, - opts: StateMigrationServiceInitOptions -): Promise { - return factory( - cache, - "stateMigrationService", - opts, - async () => - new StateMigrationService( - await diskStorageServiceFactory(cache, opts), - await secureStorageServiceFactory(cache, opts), - opts.stateMigrationServiceOptions.stateFactory - ) - ); -} diff --git a/apps/browser/src/platform/background/service-factories/state-service.factory.ts b/apps/browser/src/platform/background/service-factories/state-service.factory.ts index f926d428890..7d3aaf9b6f3 100644 --- a/apps/browser/src/platform/background/service-factories/state-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/state-service.factory.ts @@ -6,10 +6,6 @@ import { BrowserStateService } from "../../services/browser-state.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; -import { - stateMigrationServiceFactory, - StateMigrationServiceInitOptions, -} from "./state-migration-service.factory"; import { diskStorageServiceFactory, secureStorageServiceFactory, @@ -30,8 +26,7 @@ export type StateServiceInitOptions = StateServiceFactoryOptions & DiskStorageServiceInitOptions & SecureStorageServiceInitOptions & MemoryStorageServiceInitOptions & - LogServiceInitOptions & - StateMigrationServiceInitOptions; + LogServiceInitOptions; export async function stateServiceFactory( cache: { stateService?: BrowserStateService } & CachedServices, @@ -47,7 +42,6 @@ export async function stateServiceFactory( await secureStorageServiceFactory(cache, opts), await memoryStorageServiceFactory(cache, opts), await logServiceFactory(cache, opts), - await stateMigrationServiceFactory(cache, opts), opts.stateServiceOptions.stateFactory, opts.stateServiceOptions.useAccountCache ) diff --git a/apps/browser/src/platform/listeners/on-command-listener.ts b/apps/browser/src/platform/listeners/on-command-listener.ts index 65af31e173c..0e2cf03828d 100644 --- a/apps/browser/src/platform/listeners/on-command-listener.ts +++ b/apps/browser/src/platform/listeners/on-command-listener.ts @@ -47,9 +47,6 @@ const doAutoFillLogin = async (tab: chrome.tabs.Tab): Promise => { stateServiceOptions: { stateFactory: new StateFactory(GlobalState, Account), }, - stateMigrationServiceOptions: { - stateFactory: new StateFactory(GlobalState, Account), - }, apiServiceOptions: { logoutCallback: () => Promise.resolve(), }, @@ -94,9 +91,6 @@ const doGeneratePasswordToClipboard = async (tab: chrome.tabs.Tab): Promise Promise.resolve(), win: self, }, - stateMigrationServiceOptions: { - stateFactory: stateFactory, - }, stateServiceOptions: { stateFactory: stateFactory, }, diff --git a/apps/browser/src/platform/listeners/on-install-listener.ts b/apps/browser/src/platform/listeners/on-install-listener.ts index 480e811fd26..0394941e283 100644 --- a/apps/browser/src/platform/listeners/on-install-listener.ts +++ b/apps/browser/src/platform/listeners/on-install-listener.ts @@ -23,9 +23,6 @@ export async function onInstallListener(details: chrome.runtime.InstalledDetails stateServiceOptions: { stateFactory: new StateFactory(GlobalState, Account), }, - stateMigrationServiceOptions: { - stateFactory: new StateFactory(GlobalState, Account), - }, }; const environmentService = await environmentServiceFactory(cache, opts); diff --git a/apps/browser/src/platform/listeners/update-badge.ts b/apps/browser/src/platform/listeners/update-badge.ts index 89b620ad6fe..1b692eb9b97 100644 --- a/apps/browser/src/platform/listeners/update-badge.ts +++ b/apps/browser/src/platform/listeners/update-badge.ts @@ -272,9 +272,6 @@ export class UpdateBadge { stateServiceOptions: { stateFactory: new StateFactory(GlobalState, Account), }, - stateMigrationServiceOptions: { - stateFactory: new StateFactory(GlobalState, Account), - }, apiServiceOptions: { logoutCallback: () => Promise.reject("not implemented"), }, diff --git a/apps/browser/src/platform/services/browser-state.service.spec.ts b/apps/browser/src/platform/services/browser-state.service.spec.ts index d6bb83f7fb5..0712416172c 100644 --- a/apps/browser/src/platform/services/browser-state.service.spec.ts +++ b/apps/browser/src/platform/services/browser-state.service.spec.ts @@ -8,7 +8,6 @@ import { import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { State } from "@bitwarden/common/platform/models/domain/state"; -import { StateMigrationService } from "@bitwarden/common/platform/services/state-migration.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; @@ -26,7 +25,6 @@ describe("Browser State Service", () => { let secureStorageService: MockProxy; let diskStorageService: MockProxy; let logService: MockProxy; - let stateMigrationService: MockProxy; let stateFactory: MockProxy>; let useAccountCache: boolean; @@ -39,7 +37,6 @@ describe("Browser State Service", () => { secureStorageService = mock(); diskStorageService = mock(); logService = mock(); - stateMigrationService = mock(); stateFactory = mock(); // turn off account cache for tests useAccountCache = false; @@ -64,7 +61,6 @@ describe("Browser State Service", () => { secureStorageService, memoryStorageService, logService, - stateMigrationService, stateFactory, useAccountCache ); diff --git a/apps/browser/src/platform/services/browser-state.service.ts b/apps/browser/src/platform/services/browser-state.service.ts index 34fa1a1d0f3..5e356e7fbe8 100644 --- a/apps/browser/src/platform/services/browser-state.service.ts +++ b/apps/browser/src/platform/services/browser-state.service.ts @@ -1,7 +1,6 @@ import { BehaviorSubject } from "rxjs"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { StateMigrationService } from "@bitwarden/common/platform/abstractions/state-migration.service"; import { AbstractStorageService, AbstractMemoryStorageService, @@ -41,7 +40,6 @@ export class BrowserStateService secureStorageService: AbstractStorageService, memoryStorageService: AbstractMemoryStorageService, logService: LogService, - stateMigrationService: StateMigrationService, stateFactory: StateFactory, useAccountCache = true ) { @@ -50,7 +48,6 @@ export class BrowserStateService secureStorageService, memoryStorageService, logService, - stateMigrationService, stateFactory, useAccountCache ); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 191e2c78060..261f6abe37d 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -47,7 +47,6 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateMigrationService } from "@bitwarden/common/platform/abstractions/state-migration.service"; import { StateService as BaseStateServiceAbstraction, StateService, @@ -442,36 +441,23 @@ function getBgService(service: keyof MainBackground) { provide: MEMORY_STORAGE, useFactory: getBgService("memoryStorageService"), }, - { - provide: StateMigrationService, - useFactory: getBgService("stateMigrationService"), - deps: [], - }, { provide: StateServiceAbstraction, useFactory: ( storageService: AbstractStorageService, secureStorageService: AbstractStorageService, memoryStorageService: AbstractMemoryStorageService, - logService: LogServiceAbstraction, - stateMigrationService: StateMigrationService + logService: LogServiceAbstraction ) => { return new BrowserStateService( storageService, secureStorageService, memoryStorageService, logService, - stateMigrationService, new StateFactory(GlobalState, Account) ); }, - deps: [ - AbstractStorageService, - SECURE_STORAGE, - MEMORY_STORAGE, - LogServiceAbstraction, - StateMigrationService, - ], + deps: [AbstractStorageService, SECURE_STORAGE, MEMORY_STORAGE, LogServiceAbstraction], }, { provide: UsernameGenerationServiceAbstraction, diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index 1bcaa1a2acf..42ba158ee72 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -37,7 +37,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/services/environm import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { NoopMessagingService } from "@bitwarden/common/platform/services/noop-messaging.service"; -import { StateMigrationService } from "@bitwarden/common/platform/services/state-migration.service"; import { StateService } from "@bitwarden/common/platform/services/state.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; import { OrganizationUserServiceImplementation } from "@bitwarden/common/services/organization-user/organization-user.service.implementation"; @@ -136,7 +135,6 @@ export class Main { keyConnectorService: KeyConnectorService; userVerificationService: UserVerificationService; stateService: StateService; - stateMigrationService: StateMigrationService; organizationService: OrganizationService; providerService: ProviderService; twoFactorService: TwoFactorService; @@ -188,18 +186,11 @@ export class Main { this.memoryStorageService = new MemoryStorageService(); - this.stateMigrationService = new StateMigrationService( - this.storageService, - this.secureStorageService, - new StateFactory(GlobalState, Account) - ); - this.stateService = new StateService( this.storageService, this.secureStorageService, this.memoryStorageService, this.logService, - this.stateMigrationService, new StateFactory(GlobalState, Account) ); diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index ded0366dc16..42208077c33 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -28,7 +28,6 @@ import { } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/platform/abstractions/state-migration.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service"; @@ -134,7 +133,6 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); SECURE_STORAGE, MEMORY_STORAGE, LogService, - StateMigrationServiceAbstraction, STATE_FACTORY, STATE_SERVICE_USE_CACHE, ], diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 9f15d0d24d9..5107d31b1c5 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -90,7 +90,6 @@ export class Main { null, this.memoryStorageService, this.logService, - null, new StateFactory(GlobalState, Account), false // Do not use disk caching because this will get out of sync with the renderer service ); diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 03f20ad2955..b2e44d7e3db 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -17,7 +17,6 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/platform/abstractions/state-migration.service"; import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; @@ -27,7 +26,6 @@ import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@ import { PolicyListService } from "../admin-console/core/policy-list.service"; import { HtmlStorageService } from "../core/html-storage.service"; import { I18nService } from "../core/i18n.service"; -import { StateMigrationService } from "../core/state-migration.service"; import { CollectionAdminService } from "../vault/core/collection-admin.service"; import { PasswordRepromptService } from "../vault/core/password-reprompt.service"; @@ -84,11 +82,6 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service"; }, { provide: MessagingServiceAbstraction, useClass: BroadcasterMessagingService }, { provide: ModalServiceAbstraction, useClass: ModalService }, - { - provide: StateMigrationServiceAbstraction, - useClass: StateMigrationService, - deps: [AbstractStorageService, SECURE_STORAGE, STATE_FACTORY], - }, StateService, { provide: BaseStateServiceAbstraction, diff --git a/apps/web/src/app/core/state-migration.service.ts b/apps/web/src/app/core/state-migration.service.ts deleted file mode 100644 index c1d6e2ded5d..00000000000 --- a/apps/web/src/app/core/state-migration.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { StateMigrationService as BaseStateMigrationService } from "@bitwarden/common/platform/services/state-migration.service"; - -import { Account } from "./state/account"; -import { GlobalState } from "./state/global-state"; - -export class StateMigrationService extends BaseStateMigrationService { - protected async migrationStateFrom1To2(): Promise { - await super.migrateStateFrom1To2(); - const globals = (await this.get("global")) ?? this.stateFactory.createGlobal(null); - globals.rememberEmail = (await this.get("rememberEmail")) ?? globals.rememberEmail; - await this.set("global", globals); - } -} diff --git a/apps/web/src/app/core/state/state.service.ts b/apps/web/src/app/core/state/state.service.ts index 60f09ceae36..c95077bfbcc 100644 --- a/apps/web/src/app/core/state/state.service.ts +++ b/apps/web/src/app/core/state/state.service.ts @@ -7,7 +7,6 @@ import { STATE_SERVICE_USE_CACHE, } from "@bitwarden/angular/services/injection-tokens"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { StateMigrationService } from "@bitwarden/common/platform/abstractions/state-migration.service"; import { AbstractMemoryStorageService, AbstractStorageService, @@ -30,7 +29,6 @@ export class StateService extends BaseStateService { @Inject(SECURE_STORAGE) secureStorageService: AbstractStorageService, @Inject(MEMORY_STORAGE) memoryStorageService: AbstractMemoryStorageService, logService: LogService, - stateMigrationService: StateMigrationService, @Inject(STATE_FACTORY) stateFactory: StateFactory, @Inject(STATE_SERVICE_USE_CACHE) useAccountCache = true ) { @@ -39,7 +37,6 @@ export class StateService extends BaseStateService { secureStorageService, memoryStorageService, logService, - stateMigrationService, stateFactory, useAccountCache ); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 14b26ca43da..df64c25c914 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -77,7 +77,6 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/platform/abstractions/state-migration.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { ValidationService as ValidationServiceAbstraction } from "@bitwarden/common/platform/abstractions/validation.service"; @@ -94,7 +93,6 @@ import { EncryptServiceImplementation } from "@bitwarden/common/platform/service import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; -import { StateMigrationService } from "@bitwarden/common/platform/services/state-migration.service"; import { StateService } from "@bitwarden/common/platform/services/state.service"; import { ValidationService } from "@bitwarden/common/platform/services/validation.service"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; @@ -480,16 +478,10 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; SECURE_STORAGE, MEMORY_STORAGE, LogService, - StateMigrationServiceAbstraction, STATE_FACTORY, STATE_SERVICE_USE_CACHE, ], }, - { - provide: StateMigrationServiceAbstraction, - useClass: StateMigrationService, - deps: [AbstractStorageService, SECURE_STORAGE, STATE_FACTORY], - }, { provide: VaultExportServiceAbstraction, useClass: VaultExportService, diff --git a/libs/common/src/enums/index.ts b/libs/common/src/enums/index.ts index 87a688b856e..b62b3ecfa81 100644 --- a/libs/common/src/enums/index.ts +++ b/libs/common/src/enums/index.ts @@ -18,7 +18,6 @@ export * from "./notification-type.enum"; export * from "./product-type.enum"; export * from "./provider-type.enum"; export * from "./secure-note-type.enum"; -export * from "./state-version.enum"; export * from "./storage-location.enum"; export * from "./theme-type.enum"; export * from "./uri-match-type.enum"; diff --git a/libs/common/src/enums/state-version.enum.ts b/libs/common/src/enums/state-version.enum.ts deleted file mode 100644 index 927ce3a1105..00000000000 --- a/libs/common/src/enums/state-version.enum.ts +++ /dev/null @@ -1,10 +0,0 @@ -export enum StateVersion { - One = 1, // Original flat key/value pair store - Two = 2, // Move to a typed State object - Three = 3, // Fix migration of users' premium status - Four = 4, // Fix 'Never Lock' option by removing stale data - Five = 5, // Migrate to new storage of encrypted organization keys - Six = 6, // Delete account.keys.legacyEtmKey property - Seven = 7, // Remove global desktop auto prompt setting, move to account - Latest = Seven, -} diff --git a/libs/common/src/platform/abstractions/state-migration.service.ts b/libs/common/src/platform/abstractions/state-migration.service.ts deleted file mode 100644 index f16777a159f..00000000000 --- a/libs/common/src/platform/abstractions/state-migration.service.ts +++ /dev/null @@ -1,4 +0,0 @@ -export abstract class StateMigrationService { - needsMigration: () => Promise; - migrate: () => Promise; -} diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 4a2b515b74a..82813718de3 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -495,8 +495,6 @@ export abstract class StateService { setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise; getApproveLoginRequests: (options?: StorageOptions) => Promise; setApproveLoginRequests: (value: boolean, options?: StorageOptions) => Promise; - getStateVersion: () => Promise; - setStateVersion: (value: number) => Promise; getWindow: () => Promise; setWindow: (value: WindowState) => Promise; /** diff --git a/libs/common/src/platform/models/domain/global-state.ts b/libs/common/src/platform/models/domain/global-state.ts index dfe3c6c417f..30ad32124cf 100644 --- a/libs/common/src/platform/models/domain/global-state.ts +++ b/libs/common/src/platform/models/domain/global-state.ts @@ -1,5 +1,5 @@ import { EnvironmentUrls } from "../../../auth/models/domain/environment-urls"; -import { StateVersion, ThemeType } from "../../../enums"; +import { ThemeType } from "../../../enums"; import { WindowState } from "../../../models/domain/window-state"; export class GlobalState { @@ -25,7 +25,6 @@ export class GlobalState { enableBiometrics?: boolean; biometricText?: string; noAutoPromptBiometricsText?: string; - stateVersion: StateVersion = StateVersion.One; environmentUrls: EnvironmentUrls = new EnvironmentUrls(); enableTray?: boolean; enableMinimizeToTray?: boolean; diff --git a/libs/common/src/platform/services/state-migration.service.spec.ts b/libs/common/src/platform/services/state-migration.service.spec.ts deleted file mode 100644 index 7bbd19106d5..00000000000 --- a/libs/common/src/platform/services/state-migration.service.spec.ts +++ /dev/null @@ -1,216 +0,0 @@ -// eslint-disable-next-line no-restricted-imports -import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; -import { MockProxy, any, mock } from "jest-mock-extended"; - -import { StateVersion } from "../../enums"; -import { AbstractStorageService } from "../abstractions/storage.service"; -import { StateFactory } from "../factories/state-factory"; -import { Account } from "../models/domain/account"; -import { GlobalState } from "../models/domain/global-state"; - -import { StateMigrationService } from "./state-migration.service"; - -const userId = "USER_ID"; - -// Note: each test calls the private migration method for that migration, -// so that we don't accidentally run all following migrations as well - -describe("State Migration Service", () => { - let storageService: MockProxy; - let secureStorageService: SubstituteOf; - let stateFactory: SubstituteOf; - - let stateMigrationService: StateMigrationService; - - beforeEach(() => { - storageService = mock(); - secureStorageService = Substitute.for(); - stateFactory = Substitute.for(); - - stateMigrationService = new StateMigrationService( - storageService, - secureStorageService, - stateFactory - ); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - describe("StateVersion 3 to 4 migration", () => { - beforeEach(() => { - const globalVersion3: Partial = { - stateVersion: StateVersion.Three, - }; - - storageService.get.calledWith("global", any()).mockResolvedValue(globalVersion3); - storageService.get.calledWith("authenticatedAccounts", any()).mockResolvedValue([userId]); - }); - - it("clears everBeenUnlocked", async () => { - const accountVersion3: Account = { - profile: { - apiKeyClientId: null, - convertAccountToKeyConnector: null, - email: "EMAIL", - emailVerified: true, - everBeenUnlocked: true, - hasPremiumPersonally: false, - kdfIterations: 100000, - kdfType: 0, - keyHash: "KEY_HASH", - lastSync: "LAST_SYNC", - userId: userId, - usesKeyConnector: false, - forcePasswordResetReason: null, - }, - }; - - const expectedAccountVersion4: Account = { - profile: { - ...accountVersion3.profile, - }, - }; - delete expectedAccountVersion4.profile.everBeenUnlocked; - - storageService.get.calledWith(userId, any()).mockResolvedValue(accountVersion3); - - await (stateMigrationService as any).migrateStateFrom3To4(); - - expect(storageService.save).toHaveBeenCalledTimes(2); - expect(storageService.save).toHaveBeenCalledWith(userId, expectedAccountVersion4, any()); - }); - - it("updates StateVersion number", async () => { - await (stateMigrationService as any).migrateStateFrom3To4(); - - expect(storageService.save).toHaveBeenCalledWith( - "global", - { stateVersion: StateVersion.Four }, - any() - ); - expect(storageService.save).toHaveBeenCalledTimes(1); - }); - }); - - describe("StateVersion 4 to 5 migration", () => { - it("migrates organization keys to new format", async () => { - const accountVersion4 = new Account({ - keys: { - organizationKeys: { - encrypted: { - orgOneId: "orgOneEncKey", - orgTwoId: "orgTwoEncKey", - orgThreeId: "orgThreeEncKey", - }, - }, - }, - } as any); - - const expectedAccount = new Account({ - keys: { - organizationKeys: { - encrypted: { - orgOneId: { - type: "organization", - key: "orgOneEncKey", - }, - orgTwoId: { - type: "organization", - key: "orgTwoEncKey", - }, - orgThreeId: { - type: "organization", - key: "orgThreeEncKey", - }, - }, - } as any, - } as any, - }); - - const migratedAccount = await (stateMigrationService as any).migrateAccountFrom4To5( - accountVersion4 - ); - - expect(migratedAccount).toEqual(expectedAccount); - }); - }); - - describe("StateVersion 5 to 6 migration", () => { - it("deletes account.keys.legacyEtmKey value", async () => { - const accountVersion5 = new Account({ - keys: { - legacyEtmKey: "legacy key", - }, - } as any); - - const migratedAccount = await (stateMigrationService as any).migrateAccountFrom5To6( - accountVersion5 - ); - - expect(migratedAccount.keys.legacyEtmKey).toBeUndefined(); - }); - }); - - describe("StateVersion 6 to 7 migration", () => { - it("should delete global.noAutoPromptBiometrics value", async () => { - storageService.get - .calledWith("global", any()) - .mockResolvedValue({ stateVersion: StateVersion.Six, noAutoPromptBiometrics: true }); - storageService.get.calledWith("authenticatedAccounts", any()).mockResolvedValue([]); - - await stateMigrationService.migrate(); - - expect(storageService.save).toHaveBeenCalledWith( - "global", - { - stateVersion: StateVersion.Seven, - }, - any() - ); - }); - - it("should call migrateStateFrom6To7 on each account", async () => { - const accountVersion6 = new Account({ - otherStuff: "other stuff", - } as any); - - storageService.get - .calledWith("global", any()) - .mockResolvedValue({ stateVersion: StateVersion.Six, noAutoPromptBiometrics: true }); - storageService.get.calledWith("authenticatedAccounts", any()).mockResolvedValue([userId]); - storageService.get.calledWith(userId, any()).mockResolvedValue(accountVersion6); - - const migrateSpy = jest.fn(); - (stateMigrationService as any).migrateAccountFrom6To7 = migrateSpy; - - await stateMigrationService.migrate(); - - expect(migrateSpy).toHaveBeenCalledWith(true, accountVersion6); - }); - - it("should update account.settings.disableAutoBiometricsPrompt value if global is no prompt", async () => { - const result = await (stateMigrationService as any).migrateAccountFrom6To7(true, { - otherStuff: "other stuff", - }); - - expect(result).toEqual({ - otherStuff: "other stuff", - settings: { - disableAutoBiometricsPrompt: true, - }, - }); - }); - - it("should not update account.settings.disableAutoBiometricsPrompt value if global auto prompt is enabled", async () => { - const result = await (stateMigrationService as any).migrateAccountFrom6To7(false, { - otherStuff: "other stuff", - }); - - expect(result).toEqual({ - otherStuff: "other stuff", - }); - }); - }); -}); diff --git a/libs/common/src/platform/services/state-migration.service.ts b/libs/common/src/platform/services/state-migration.service.ts deleted file mode 100644 index 234d1b2bff8..00000000000 --- a/libs/common/src/platform/services/state-migration.service.ts +++ /dev/null @@ -1,587 +0,0 @@ -import { OrganizationData } from "../../admin-console/models/data/organization.data"; -import { PolicyData } from "../../admin-console/models/data/policy.data"; -import { ProviderData } from "../../admin-console/models/data/provider.data"; -import { EnvironmentUrls } from "../../auth/models/domain/environment-urls"; -import { TokenService } from "../../auth/services/token.service"; -import { StateVersion, ThemeType, KdfType, HtmlStorageLocation } from "../../enums"; -import { EventData } from "../../models/data/event.data"; -import { GeneratedPasswordHistory } from "../../tools/generator/password"; -import { SendData } from "../../tools/send/models/data/send.data"; -import { CipherData } from "../../vault/models/data/cipher.data"; -import { CollectionData } from "../../vault/models/data/collection.data"; -import { FolderData } from "../../vault/models/data/folder.data"; -import { AbstractStorageService } from "../abstractions/storage.service"; -import { StateFactory } from "../factories/state-factory"; -import { - Account, - AccountSettings, - EncryptionPair, - AccountSettingsSettings, -} from "../models/domain/account"; -import { EncString } from "../models/domain/enc-string"; -import { GlobalState } from "../models/domain/global-state"; -import { StorageOptions } from "../models/domain/storage-options"; - -// Originally (before January 2022) storage was handled as a flat key/value pair store. -// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration. -const v1Keys: { [key: string]: string } = { - accessToken: "accessToken", - alwaysShowDock: "alwaysShowDock", - autoConfirmFingerprints: "autoConfirmFingerprints", - autoFillOnPageLoadDefault: "autoFillOnPageLoadDefault", - biometricAwaitingAcceptance: "biometricAwaitingAcceptance", - biometricFingerprintValidated: "biometricFingerprintValidated", - biometricText: "biometricText", - biometricUnlock: "biometric", - clearClipboard: "clearClipboardKey", - clientId: "apikey_clientId", - clientSecret: "apikey_clientSecret", - collapsedGroupings: "collapsedGroupings", - convertAccountToKeyConnector: "convertAccountToKeyConnector", - defaultUriMatch: "defaultUriMatch", - disableAddLoginNotification: "disableAddLoginNotification", - disableAutoBiometricsPrompt: "noAutoPromptBiometrics", - disableAutoTotpCopy: "disableAutoTotpCopy", - disableBadgeCounter: "disableBadgeCounter", - disableChangedPasswordNotification: "disableChangedPasswordNotification", - disableContextMenuItem: "disableContextMenuItem", - disableFavicon: "disableFavicon", - disableGa: "disableGa", - dontShowCardsCurrentTab: "dontShowCardsCurrentTab", - dontShowIdentitiesCurrentTab: "dontShowIdentitiesCurrentTab", - emailVerified: "emailVerified", - enableAlwaysOnTop: "enableAlwaysOnTopKey", - enableAutoFillOnPageLoad: "enableAutoFillOnPageLoad", - enableBiometric: "enabledBiometric", - enableBrowserIntegration: "enableBrowserIntegration", - enableBrowserIntegrationFingerprint: "enableBrowserIntegrationFingerprint", - enableCloseToTray: "enableCloseToTray", - enableFullWidth: "enableFullWidth", - enableMinimizeToTray: "enableMinimizeToTray", - enableStartToTray: "enableStartToTrayKey", - enableTray: "enableTray", - encKey: "encKey", // Generated Symmetric Key - encOrgKeys: "encOrgKeys", - encPrivate: "encPrivateKey", - encProviderKeys: "encProviderKeys", - entityId: "entityId", - entityType: "entityType", - environmentUrls: "environmentUrls", - equivalentDomains: "equivalentDomains", - eventCollection: "eventCollection", - forcePasswordReset: "forcePasswordReset", - history: "generatedPasswordHistory", - installedVersion: "installedVersion", - kdf: "kdf", - kdfIterations: "kdfIterations", - key: "key", // Master Key - keyHash: "keyHash", - lastActive: "lastActive", - localData: "sitesLocalData", - locale: "locale", - mainWindowSize: "mainWindowSize", - minimizeOnCopyToClipboard: "minimizeOnCopyToClipboardKey", - neverDomains: "neverDomains", - noAutoPromptBiometricsText: "noAutoPromptBiometricsText", - openAtLogin: "openAtLogin", - passwordGenerationOptions: "passwordGenerationOptions", - pinProtected: "pinProtectedKey", - protectedPin: "protectedPin", - refreshToken: "refreshToken", - ssoCodeVerifier: "ssoCodeVerifier", - ssoIdentifier: "ssoOrgIdentifier", - ssoState: "ssoState", - stamp: "securityStamp", - theme: "theme", - userEmail: "userEmail", - userId: "userId", - usesConnector: "usesKeyConnector", - vaultTimeoutAction: "vaultTimeoutAction", - vaultTimeout: "lockOption", - rememberedEmail: "rememberedEmail", -}; - -const v1KeyPrefixes: { [key: string]: string } = { - ciphers: "ciphers_", - collections: "collections_", - folders: "folders_", - lastSync: "lastSync_", - policies: "policies_", - twoFactorToken: "twoFactorToken_", - organizations: "organizations_", - providers: "providers_", - sends: "sends_", - settings: "settings_", -}; - -const keys = { - global: "global", - authenticatedAccounts: "authenticatedAccounts", - activeUserId: "activeUserId", - tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication - accountActivity: "accountActivity", -}; - -const partialKeys = { - autoKey: "_masterkey_auto", - biometricKey: "_masterkey_biometric", - masterKey: "_masterkey", -}; - -export class StateMigrationService< - TGlobalState extends GlobalState = GlobalState, - TAccount extends Account = Account -> { - constructor( - protected storageService: AbstractStorageService, - protected secureStorageService: AbstractStorageService, - protected stateFactory: StateFactory - ) {} - - async needsMigration(): Promise { - const currentStateVersion = await this.getCurrentStateVersion(); - return currentStateVersion == null || currentStateVersion < StateVersion.Latest; - } - - async migrate(): Promise { - let currentStateVersion = await this.getCurrentStateVersion(); - while (currentStateVersion < StateVersion.Latest) { - switch (currentStateVersion) { - case StateVersion.One: - await this.migrateStateFrom1To2(); - break; - case StateVersion.Two: - await this.migrateStateFrom2To3(); - break; - case StateVersion.Three: - await this.migrateStateFrom3To4(); - break; - case StateVersion.Four: { - const authenticatedAccounts = await this.getAuthenticatedAccounts(); - for (const account of authenticatedAccounts) { - const migratedAccount = await this.migrateAccountFrom4To5(account); - await this.set(account.profile.userId, migratedAccount); - } - await this.setCurrentStateVersion(StateVersion.Five); - break; - } - case StateVersion.Five: { - const authenticatedAccounts = await this.getAuthenticatedAccounts(); - for (const account of authenticatedAccounts) { - const migratedAccount = await this.migrateAccountFrom5To6(account); - await this.set(account.profile.userId, migratedAccount); - } - await this.setCurrentStateVersion(StateVersion.Six); - break; - } - case StateVersion.Six: { - const authenticatedAccounts = await this.getAuthenticatedAccounts(); - const globals = (await this.getGlobals()) as any; - for (const account of authenticatedAccounts) { - const migratedAccount = await this.migrateAccountFrom6To7( - globals?.noAutoPromptBiometrics, - account - ); - await this.set(account.profile.userId, migratedAccount); - } - if (globals) { - delete globals.noAutoPromptBiometrics; - } - await this.set(keys.global, globals); - await this.setCurrentStateVersion(StateVersion.Seven); - } - } - - currentStateVersion += 1; - } - } - - protected async migrateStateFrom1To2(): Promise { - const clearV1Keys = async (clearingUserId?: string) => { - for (const key in v1Keys) { - if (key == null) { - continue; - } - await this.set(v1Keys[key], null); - } - if (clearingUserId != null) { - for (const keyPrefix in v1KeyPrefixes) { - if (keyPrefix == null) { - continue; - } - await this.set(v1KeyPrefixes[keyPrefix] + userId, null); - } - } - }; - - // Some processes, like biometrics, may have already defined a value before migrations are run. - // We don't want to null out those values if they don't exist in the old storage scheme (like for new installs) - // So, the OOO for migration is that we: - // 1. Check for an existing storage value from the old storage structure OR - // 2. Check for a value already set by processes that run before migration OR - // 3. Assign the default value - const globals: any = - (await this.get(keys.global)) ?? this.stateFactory.createGlobal(null); - globals.stateVersion = StateVersion.Two; - globals.environmentUrls = - (await this.get(v1Keys.environmentUrls)) ?? globals.environmentUrls; - globals.locale = (await this.get(v1Keys.locale)) ?? globals.locale; - globals.noAutoPromptBiometrics = - (await this.get(v1Keys.disableAutoBiometricsPrompt)) ?? - globals.noAutoPromptBiometrics; - globals.noAutoPromptBiometricsText = - (await this.get(v1Keys.noAutoPromptBiometricsText)) ?? - globals.noAutoPromptBiometricsText; - globals.ssoCodeVerifier = - (await this.get(v1Keys.ssoCodeVerifier)) ?? globals.ssoCodeVerifier; - globals.ssoOrganizationIdentifier = - (await this.get(v1Keys.ssoIdentifier)) ?? globals.ssoOrganizationIdentifier; - globals.ssoState = (await this.get(v1Keys.ssoState)) ?? globals.ssoState; - globals.rememberedEmail = - (await this.get(v1Keys.rememberedEmail)) ?? globals.rememberedEmail; - globals.theme = (await this.get(v1Keys.theme)) ?? globals.theme; - globals.vaultTimeout = (await this.get(v1Keys.vaultTimeout)) ?? globals.vaultTimeout; - globals.vaultTimeoutAction = - (await this.get(v1Keys.vaultTimeoutAction)) ?? globals.vaultTimeoutAction; - globals.window = (await this.get(v1Keys.mainWindowSize)) ?? globals.window; - globals.enableTray = (await this.get(v1Keys.enableTray)) ?? globals.enableTray; - globals.enableMinimizeToTray = - (await this.get(v1Keys.enableMinimizeToTray)) ?? globals.enableMinimizeToTray; - globals.enableCloseToTray = - (await this.get(v1Keys.enableCloseToTray)) ?? globals.enableCloseToTray; - globals.enableStartToTray = - (await this.get(v1Keys.enableStartToTray)) ?? globals.enableStartToTray; - globals.openAtLogin = (await this.get(v1Keys.openAtLogin)) ?? globals.openAtLogin; - globals.alwaysShowDock = - (await this.get(v1Keys.alwaysShowDock)) ?? globals.alwaysShowDock; - globals.enableBrowserIntegration = - (await this.get(v1Keys.enableBrowserIntegration)) ?? - globals.enableBrowserIntegration; - globals.enableBrowserIntegrationFingerprint = - (await this.get(v1Keys.enableBrowserIntegrationFingerprint)) ?? - globals.enableBrowserIntegrationFingerprint; - - const userId = - (await this.get(v1Keys.userId)) ?? (await this.get(v1Keys.entityId)); - - const defaultAccount = this.stateFactory.createAccount(null); - const accountSettings: AccountSettings = { - autoConfirmFingerPrints: - (await this.get(v1Keys.autoConfirmFingerprints)) ?? - defaultAccount.settings.autoConfirmFingerPrints, - autoFillOnPageLoadDefault: - (await this.get(v1Keys.autoFillOnPageLoadDefault)) ?? - defaultAccount.settings.autoFillOnPageLoadDefault, - biometricUnlock: - (await this.get(v1Keys.biometricUnlock)) ?? - defaultAccount.settings.biometricUnlock, - clearClipboard: - (await this.get(v1Keys.clearClipboard)) ?? defaultAccount.settings.clearClipboard, - defaultUriMatch: - (await this.get(v1Keys.defaultUriMatch)) ?? defaultAccount.settings.defaultUriMatch, - disableAddLoginNotification: - (await this.get(v1Keys.disableAddLoginNotification)) ?? - defaultAccount.settings.disableAddLoginNotification, - disableAutoBiometricsPrompt: - (await this.get(v1Keys.disableAutoBiometricsPrompt)) ?? - defaultAccount.settings.disableAutoBiometricsPrompt, - disableAutoTotpCopy: - (await this.get(v1Keys.disableAutoTotpCopy)) ?? - defaultAccount.settings.disableAutoTotpCopy, - disableBadgeCounter: - (await this.get(v1Keys.disableBadgeCounter)) ?? - defaultAccount.settings.disableBadgeCounter, - disableChangedPasswordNotification: - (await this.get(v1Keys.disableChangedPasswordNotification)) ?? - defaultAccount.settings.disableChangedPasswordNotification, - disableContextMenuItem: - (await this.get(v1Keys.disableContextMenuItem)) ?? - defaultAccount.settings.disableContextMenuItem, - disableGa: (await this.get(v1Keys.disableGa)) ?? defaultAccount.settings.disableGa, - dontShowCardsCurrentTab: - (await this.get(v1Keys.dontShowCardsCurrentTab)) ?? - defaultAccount.settings.dontShowCardsCurrentTab, - dontShowIdentitiesCurrentTab: - (await this.get(v1Keys.dontShowIdentitiesCurrentTab)) ?? - defaultAccount.settings.dontShowIdentitiesCurrentTab, - enableAlwaysOnTop: - (await this.get(v1Keys.enableAlwaysOnTop)) ?? - defaultAccount.settings.enableAlwaysOnTop, - enableAutoFillOnPageLoad: - (await this.get(v1Keys.enableAutoFillOnPageLoad)) ?? - defaultAccount.settings.enableAutoFillOnPageLoad, - enableBiometric: - (await this.get(v1Keys.enableBiometric)) ?? - defaultAccount.settings.enableBiometric, - enableFullWidth: - (await this.get(v1Keys.enableFullWidth)) ?? - defaultAccount.settings.enableFullWidth, - environmentUrls: globals.environmentUrls ?? defaultAccount.settings.environmentUrls, - equivalentDomains: - (await this.get(v1Keys.equivalentDomains)) ?? - defaultAccount.settings.equivalentDomains, - minimizeOnCopyToClipboard: - (await this.get(v1Keys.minimizeOnCopyToClipboard)) ?? - defaultAccount.settings.minimizeOnCopyToClipboard, - neverDomains: - (await this.get(v1Keys.neverDomains)) ?? defaultAccount.settings.neverDomains, - passwordGenerationOptions: - (await this.get(v1Keys.passwordGenerationOptions)) ?? - defaultAccount.settings.passwordGenerationOptions, - pinProtected: Object.assign(new EncryptionPair(), { - decrypted: null, - encrypted: await this.get(v1Keys.pinProtected), - }), - protectedPin: await this.get(v1Keys.protectedPin), - settings: - userId == null - ? null - : await this.get(v1KeyPrefixes.settings + userId), - vaultTimeout: - (await this.get(v1Keys.vaultTimeout)) ?? defaultAccount.settings.vaultTimeout, - vaultTimeoutAction: - (await this.get(v1Keys.vaultTimeoutAction)) ?? - defaultAccount.settings.vaultTimeoutAction, - }; - - // (userId == null) = no logged in user (so no known userId) and we need to temporarily store account specific settings in state to migrate on first auth - // (userId != null) = we have a currently authed user (so known userId) with encrypted data and other key settings we can move, no need to temporarily store account settings - if (userId == null) { - await this.set(keys.tempAccountSettings, accountSettings); - await this.set(keys.global, globals); - await this.set(keys.authenticatedAccounts, []); - await this.set(keys.activeUserId, null); - await clearV1Keys(); - return; - } - - globals.twoFactorToken = await this.get(v1KeyPrefixes.twoFactorToken + userId); - await this.set(keys.global, globals); - await this.set(userId, { - data: { - addEditCipherInfo: null, - ciphers: { - decrypted: null, - encrypted: await this.get<{ [id: string]: CipherData }>(v1KeyPrefixes.ciphers + userId), - }, - collapsedGroupings: null, - collections: { - decrypted: null, - encrypted: await this.get<{ [id: string]: CollectionData }>( - v1KeyPrefixes.collections + userId - ), - }, - eventCollection: await this.get(v1Keys.eventCollection), - folders: { - decrypted: null, - encrypted: await this.get<{ [id: string]: FolderData }>(v1KeyPrefixes.folders + userId), - }, - localData: null, - organizations: await this.get<{ [id: string]: OrganizationData }>( - v1KeyPrefixes.organizations + userId - ), - passwordGenerationHistory: { - decrypted: null, - encrypted: await this.get(v1Keys.history), - }, - policies: { - decrypted: null, - encrypted: await this.get<{ [id: string]: PolicyData }>(v1KeyPrefixes.policies + userId), - }, - providers: await this.get<{ [id: string]: ProviderData }>(v1KeyPrefixes.providers + userId), - sends: { - decrypted: null, - encrypted: await this.get<{ [id: string]: SendData }>(v1KeyPrefixes.sends + userId), - }, - }, - keys: { - apiKeyClientSecret: await this.get(v1Keys.clientSecret), - cryptoMasterKey: null, - cryptoMasterKeyAuto: null, - cryptoMasterKeyB64: null, - cryptoMasterKeyBiometric: null, - cryptoSymmetricKey: { - encrypted: await this.get(v1Keys.encKey), - decrypted: null, - }, - legacyEtmKey: null, - organizationKeys: { - decrypted: null, - encrypted: await this.get(v1Keys.encOrgKeys), - }, - privateKey: { - decrypted: null, - encrypted: await this.get(v1Keys.encPrivate), - }, - providerKeys: { - decrypted: null, - encrypted: await this.get(v1Keys.encProviderKeys), - }, - publicKey: null, - }, - profile: { - apiKeyClientId: await this.get(v1Keys.clientId), - authenticationStatus: null, - convertAccountToKeyConnector: await this.get(v1Keys.convertAccountToKeyConnector), - email: await this.get(v1Keys.userEmail), - emailVerified: await this.get(v1Keys.emailVerified), - entityId: null, - entityType: null, - everBeenUnlocked: null, - forcePasswordReset: null, - hasPremiumPersonally: null, - kdfIterations: await this.get(v1Keys.kdfIterations), - kdfType: await this.get(v1Keys.kdf), - keyHash: await this.get(v1Keys.keyHash), - lastSync: null, - userId: userId, - usesKeyConnector: null, - }, - settings: accountSettings, - tokens: { - accessToken: await this.get(v1Keys.accessToken), - decodedToken: null, - refreshToken: await this.get(v1Keys.refreshToken), - securityStamp: null, - }, - }); - - await this.set(keys.authenticatedAccounts, [userId]); - await this.set(keys.activeUserId, userId); - - const accountActivity: { [userId: string]: number } = { - [userId]: await this.get(v1Keys.lastActive), - }; - accountActivity[userId] = await this.get(v1Keys.lastActive); - await this.set(keys.accountActivity, accountActivity); - - await clearV1Keys(userId); - - if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "biometric" })) { - await this.secureStorageService.save( - `${userId}${partialKeys.biometricKey}`, - await this.secureStorageService.get(v1Keys.key, { keySuffix: "biometric" }), - { keySuffix: "biometric" } - ); - await this.secureStorageService.remove(v1Keys.key, { keySuffix: "biometric" }); - } - - if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "auto" })) { - await this.secureStorageService.save( - `${userId}${partialKeys.autoKey}`, - await this.secureStorageService.get(v1Keys.key, { keySuffix: "auto" }), - { keySuffix: "auto" } - ); - await this.secureStorageService.remove(v1Keys.key, { keySuffix: "auto" }); - } - - if (await this.secureStorageService.has(v1Keys.key)) { - await this.secureStorageService.save( - `${userId}${partialKeys.masterKey}`, - await this.secureStorageService.get(v1Keys.key) - ); - await this.secureStorageService.remove(v1Keys.key); - } - } - - protected async migrateStateFrom2To3(): Promise { - const authenticatedUserIds = await this.get(keys.authenticatedAccounts); - await Promise.all( - authenticatedUserIds.map(async (userId) => { - const account = await this.get(userId); - if ( - account?.profile?.hasPremiumPersonally === null && - account.tokens?.accessToken != null - ) { - const decodedToken = await TokenService.decodeToken(account.tokens.accessToken); - account.profile.hasPremiumPersonally = decodedToken.premium; - await this.set(userId, account); - } - }) - ); - - const globals = await this.getGlobals(); - globals.stateVersion = StateVersion.Three; - await this.set(keys.global, globals); - } - - protected async migrateStateFrom3To4(): Promise { - const authenticatedUserIds = await this.get(keys.authenticatedAccounts); - await Promise.all( - authenticatedUserIds.map(async (userId) => { - const account = await this.get(userId); - if (account?.profile?.everBeenUnlocked != null) { - delete account.profile.everBeenUnlocked; - return this.set(userId, account); - } - }) - ); - - const globals = await this.getGlobals(); - globals.stateVersion = StateVersion.Four; - await this.set(keys.global, globals); - } - - protected async migrateAccountFrom4To5(account: TAccount): Promise { - const encryptedOrgKeys = account.keys?.organizationKeys?.encrypted; - if (encryptedOrgKeys != null) { - for (const [orgId, encKey] of Object.entries(encryptedOrgKeys)) { - encryptedOrgKeys[orgId] = { - type: "organization", - key: encKey as unknown as string, // Account v4 does not reflect the current account model so we have to cast - }; - } - } - - return account; - } - - protected async migrateAccountFrom5To6(account: TAccount): Promise { - delete (account as any).keys?.legacyEtmKey; - return account; - } - - protected async migrateAccountFrom6To7( - globalSetting: boolean, - account: TAccount - ): Promise { - if (globalSetting) { - account.settings = Object.assign({}, account.settings, { disableAutoBiometricsPrompt: true }); - } - return account; - } - - protected get options(): StorageOptions { - return { htmlStorageLocation: HtmlStorageLocation.Local }; - } - - protected get(key: string): Promise { - return this.storageService.get(key, this.options); - } - - protected set(key: string, value: any): Promise { - if (value == null) { - return this.storageService.remove(key, this.options); - } - return this.storageService.save(key, value, this.options); - } - - protected async getGlobals(): Promise { - return await this.get(keys.global); - } - - protected async getCurrentStateVersion(): Promise { - return (await this.getGlobals())?.stateVersion ?? StateVersion.One; - } - - protected async setCurrentStateVersion(newVersion: StateVersion): Promise { - const globals = await this.getGlobals(); - globals.stateVersion = newVersion; - await this.set(keys.global, globals); - } - - protected async getAuthenticatedAccounts(): Promise { - const authenticatedUserIds = await this.get(keys.authenticatedAccounts); - return Promise.all(authenticatedUserIds.map((id) => this.get(id))); - } -} diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index 14ba4abfd39..5fdf40e8458 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -21,6 +21,7 @@ import { import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; import { EventData } from "../../models/data/event.data"; import { WindowState } from "../../models/domain/window-state"; +import { migrate } from "../../state-migrations"; import { GeneratedPasswordHistory } from "../../tools/generator/password"; import { SendData } from "../../tools/send/models/data/send.data"; import { SendView } from "../../tools/send/models/view/send.view"; @@ -32,7 +33,6 @@ import { CipherView } from "../../vault/models/view/cipher.view"; import { CollectionView } from "../../vault/models/view/collection.view"; import { AddEditCipherInfo } from "../../vault/types/add-edit-cipher-info"; import { LogService } from "../abstractions/log.service"; -import { StateMigrationService } from "../abstractions/state-migration.service"; import { StateService as StateServiceAbstraction } from "../abstractions/state.service"; import { AbstractMemoryStorageService, @@ -61,6 +61,7 @@ import { const keys = { state: "state", + stateVersion: "stateVersion", global: "global", authenticatedAccounts: "authenticatedAccounts", activeUserId: "activeUserId", @@ -106,7 +107,6 @@ export class StateService< protected secureStorageService: AbstractStorageService, protected memoryStorageService: AbstractMemoryStorageService, protected logService: LogService, - protected stateMigrationService: StateMigrationService, protected stateFactory: StateFactory, protected useAccountCache: boolean = true ) { @@ -133,9 +133,7 @@ export class StateService< return; } - if (await this.stateMigrationService.needsMigration()) { - await this.stateMigrationService.migrate(); - } + await migrate(this.storageService, this.logService); await this.state().then(async (state) => { if (state == null) { @@ -2724,16 +2722,6 @@ export class StateService< ); } - async getStateVersion(): Promise { - return (await this.getGlobals(await this.defaultOnDiskLocalOptions())).stateVersion ?? 1; - } - - async setStateVersion(value: number): Promise { - const globals = await this.getGlobals(await this.defaultOnDiskOptions()); - globals.stateVersion = value; - await this.saveGlobals(globals, await this.defaultOnDiskOptions()); - } - async getWindow(): Promise { const globals = await this.getGlobals(await this.defaultOnDiskOptions()); return globals?.window != null && Object.keys(globals.window).length > 0 @@ -2838,7 +2826,11 @@ export class StateService< globals = await this.getGlobalsFromDisk(options); } - return globals ?? this.createGlobals(); + if (globals == null) { + globals = this.createGlobals(); + } + + return globals; } protected async saveGlobals(globals: TGlobalState, options: StorageOptions) { diff --git a/libs/common/src/state-migrations/.eslintrc.json b/libs/common/src/state-migrations/.eslintrc.json new file mode 100644 index 00000000000..4b66f0a32fa --- /dev/null +++ b/libs/common/src/state-migrations/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "overrides": [ + { + "files": ["*"], + "rules": { + "import/no-restricted-paths": [ + "error", + { + "basePath": "libs/common/src/state-migrations", + "zones": [ + { + "target": "./", + "from": "../", + // Relative to from, not basePath + "except": ["state-migrations"], + "message": "State migrations should rarely import from the greater codebase. If you need to import from another location, take into account the likelihood of change in that code and consider copying to the migration instead." + } + ] + } + ] + } + } + ] +} diff --git a/libs/common/src/state-migrations/index.ts b/libs/common/src/state-migrations/index.ts new file mode 100644 index 00000000000..c883b1ca811 --- /dev/null +++ b/libs/common/src/state-migrations/index.ts @@ -0,0 +1 @@ +export { migrate, CURRENT_VERSION } from "./migrate"; diff --git a/libs/common/src/state-migrations/migrate.spec.ts b/libs/common/src/state-migrations/migrate.spec.ts new file mode 100644 index 00000000000..ade3d261f69 --- /dev/null +++ b/libs/common/src/state-migrations/migrate.spec.ts @@ -0,0 +1,67 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +// eslint-disable-next-line import/no-restricted-paths -- Needed to print log messages +import { LogService } from "../platform/abstractions/log.service"; +// eslint-disable-next-line import/no-restricted-paths -- Needed to interface with storage locations +import { AbstractStorageService } from "../platform/abstractions/storage.service"; + +import { CURRENT_VERSION, currentVersion, migrate } from "./migrate"; +import { MigrationBuilder } from "./migration-builder"; + +jest.mock("./migration-builder", () => { + return { + MigrationBuilder: { + create: jest.fn().mockReturnThis(), + }, + }; +}); + +describe("migrate", () => { + it("should not run migrations if state is empty", async () => { + const storage = mock(); + const logService = mock(); + storage.get.mockReturnValueOnce(null); + await migrate(storage, logService); + expect(MigrationBuilder.create).not.toHaveBeenCalled(); + }); + + it("should set to current version if state is empty", async () => { + const storage = mock(); + const logService = mock(); + storage.get.mockReturnValueOnce(null); + await migrate(storage, logService); + expect(storage.save).toHaveBeenCalledWith("stateVersion", CURRENT_VERSION); + }); +}); + +describe("currentVersion", () => { + let storage: MockProxy; + let logService: MockProxy; + + beforeEach(() => { + storage = mock(); + logService = mock(); + }); + + it("should return -1 if no version", async () => { + storage.get.mockReturnValueOnce(null); + expect(await currentVersion(storage, logService)).toEqual(-1); + }); + + it("should return version", async () => { + storage.get.calledWith("stateVersion").mockReturnValueOnce(1 as any); + expect(await currentVersion(storage, logService)).toEqual(1); + }); + + it("should return version from global", async () => { + storage.get.calledWith("stateVersion").mockReturnValueOnce(null); + storage.get.calledWith("global").mockReturnValueOnce({ stateVersion: 1 } as any); + expect(await currentVersion(storage, logService)).toEqual(1); + }); + + it("should prefer root version to global", async () => { + storage.get.calledWith("stateVersion").mockReturnValue(1 as any); + storage.get.calledWith("global").mockReturnValue({ stateVersion: 2 } as any); + expect(await currentVersion(storage, logService)).toEqual(1); + }); +}); diff --git a/libs/common/src/state-migrations/migrate.ts b/libs/common/src/state-migrations/migrate.ts new file mode 100644 index 00000000000..483c4f2e8eb --- /dev/null +++ b/libs/common/src/state-migrations/migrate.ts @@ -0,0 +1,60 @@ +// eslint-disable-next-line import/no-restricted-paths -- Needed to print log messages +import { LogService } from "../platform/abstractions/log.service"; +// eslint-disable-next-line import/no-restricted-paths -- Needed to interface with storage locations +import { AbstractStorageService } from "../platform/abstractions/storage.service"; + +import { MigrationBuilder } from "./migration-builder"; +import { MigrationHelper } from "./migration-helper"; +import { FixPremiumMigrator } from "./migrations/3-fix-premium"; +import { RemoveEverBeenUnlockedMigrator } from "./migrations/4-remove-ever-been-unlocked"; +import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys"; +import { RemoveLegacyEtmKeyMigrator } from "./migrations/6-remove-legacy-etm-key"; +import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account"; +import { MoveStateVersionMigrator } from "./migrations/8-move-state-version"; +import { MinVersionMigrator } from "./migrations/min-version"; + +export const MIN_VERSION = 2; +export const CURRENT_VERSION = 8; +export type MinVersion = typeof MIN_VERSION; + +export async function migrate( + storageService: AbstractStorageService, + logService: LogService +): Promise { + const migrationHelper = new MigrationHelper( + await currentVersion(storageService, logService), + storageService, + logService + ); + if (migrationHelper.currentVersion < 0) { + // Cannot determine state, assuming empty so we don't repeatedly apply a migration. + await storageService.save("stateVersion", CURRENT_VERSION); + return; + } + MigrationBuilder.create() + .with(MinVersionMigrator) + .with(FixPremiumMigrator, 2, 3) + .with(RemoveEverBeenUnlockedMigrator, 3, 4) + .with(AddKeyTypeToOrgKeysMigrator, 4, 5) + .with(RemoveLegacyEtmKeyMigrator, 5, 6) + .with(MoveBiometricAutoPromptToAccount, 6, 7) + .with(MoveStateVersionMigrator, 7, CURRENT_VERSION) + .migrate(migrationHelper); +} + +export async function currentVersion( + storageService: AbstractStorageService, + logService: LogService +) { + let state = await storageService.get("stateVersion"); + if (state == null) { + // Pre v8 + state = (await storageService.get<{ stateVersion: number }>("global"))?.stateVersion; + } + if (state == null) { + logService.info("No state version found, assuming empty state."); + return -1; + } + logService.info(`State version: ${state}`); + return state; +} diff --git a/libs/common/src/state-migrations/migration-builder.spec.ts b/libs/common/src/state-migrations/migration-builder.spec.ts new file mode 100644 index 00000000000..fa53544f133 --- /dev/null +++ b/libs/common/src/state-migrations/migration-builder.spec.ts @@ -0,0 +1,117 @@ +import { mock } from "jest-mock-extended"; + +import { MigrationBuilder } from "./migration-builder"; +import { MigrationHelper } from "./migration-helper"; +import { Migrator } from "./migrator"; + +describe("MigrationBuilder", () => { + class TestMigrator extends Migrator<0, 1> { + async migrate(helper: MigrationHelper): Promise { + await helper.set("test", "test"); + return; + } + + async rollback(helper: MigrationHelper): Promise { + await helper.set("test", "rollback"); + return; + } + } + + let sut: MigrationBuilder; + + beforeEach(() => { + sut = MigrationBuilder.create(); + }); + + class TestBadMigrator extends Migrator<1, 0> { + async migrate(helper: MigrationHelper): Promise { + await helper.set("test", "test"); + } + + async rollback(helper: MigrationHelper): Promise { + await helper.set("test", "rollback"); + } + } + + it("should throw if instantiated incorrectly", () => { + expect(() => MigrationBuilder.create().with(TestMigrator, null, null)).toThrow(); + expect(() => + MigrationBuilder.create().with(TestMigrator, 0, 1).with(TestBadMigrator, 1, 0) + ).toThrow(); + }); + + it("should be able to create a new MigrationBuilder", () => { + expect(sut).toBeInstanceOf(MigrationBuilder); + }); + + it("should be able to add a migrator", () => { + const newBuilder = sut.with(TestMigrator, 0, 1); + const migrations = newBuilder["migrations"]; + expect(migrations.length).toBe(1); + expect(migrations[0]).toMatchObject({ migrator: expect.any(TestMigrator), direction: "up" }); + }); + + it("should be able to add a rollback", () => { + const newBuilder = sut.with(TestMigrator, 0, 1).rollback(TestMigrator, 1, 0); + const migrations = newBuilder["migrations"]; + expect(migrations.length).toBe(2); + expect(migrations[1]).toMatchObject({ migrator: expect.any(TestMigrator), direction: "down" }); + }); + + describe("migrate", () => { + let migrator: TestMigrator; + let rollback_migrator: TestMigrator; + + beforeEach(() => { + sut = sut.with(TestMigrator, 0, 1).rollback(TestMigrator, 1, 0); + migrator = (sut as any).migrations[0].migrator; + rollback_migrator = (sut as any).migrations[1].migrator; + }); + + it("should migrate", async () => { + const helper = new MigrationHelper(0, mock(), mock()); + const spy = jest.spyOn(migrator, "migrate"); + await sut.migrate(helper); + expect(spy).toBeCalledWith(helper); + }); + + it("should rollback", async () => { + const helper = new MigrationHelper(1, mock(), mock()); + const spy = jest.spyOn(rollback_migrator, "rollback"); + await sut.migrate(helper); + expect(spy).toBeCalledWith(helper); + }); + + it("should update version on migrate", async () => { + const helper = new MigrationHelper(0, mock(), mock()); + const spy = jest.spyOn(migrator, "updateVersion"); + await sut.migrate(helper); + expect(spy).toBeCalledWith(helper, "up"); + }); + + it("should update version on rollback", async () => { + const helper = new MigrationHelper(1, mock(), mock()); + const spy = jest.spyOn(rollback_migrator, "updateVersion"); + await sut.migrate(helper); + expect(spy).toBeCalledWith(helper, "down"); + }); + + it("should not run the migrator if the current version does not match the from version", async () => { + const helper = new MigrationHelper(3, mock(), mock()); + const migrate = jest.spyOn(migrator, "migrate"); + const rollback = jest.spyOn(rollback_migrator, "rollback"); + await sut.migrate(helper); + expect(migrate).not.toBeCalled(); + expect(rollback).not.toBeCalled(); + }); + + it("should not update version if the current version does not match the from version", async () => { + const helper = new MigrationHelper(3, mock(), mock()); + const migrate = jest.spyOn(migrator, "updateVersion"); + const rollback = jest.spyOn(rollback_migrator, "updateVersion"); + await sut.migrate(helper); + expect(migrate).not.toBeCalled(); + expect(rollback).not.toBeCalled(); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migration-builder.ts b/libs/common/src/state-migrations/migration-builder.ts new file mode 100644 index 00000000000..776295a6b8f --- /dev/null +++ b/libs/common/src/state-migrations/migration-builder.ts @@ -0,0 +1,106 @@ +import { MigrationHelper } from "./migration-helper"; +import { Direction, Migrator, VersionFrom, VersionTo } from "./migrator"; + +export class MigrationBuilder { + /** Create a new MigrationBuilder with an empty buffer of migrations to perform. + * + * Add migrations to the buffer with {@link with} and {@link rollback}. + * @returns A new MigrationBuilder. + */ + static create(): MigrationBuilder<0> { + return new MigrationBuilder([]); + } + + private constructor( + private migrations: readonly { migrator: Migrator; direction: Direction }[] + ) {} + + /** Add a migrator to the MigrationBuilder. Types are updated such that the chained MigrationBuilder must currently be + * at state version equal to the from version of the migrator. Return as MigrationBuilder where TTo is the to + * version of the migrator, so that the next migrator can be chained. + * + * @param migrate A migrator class or a tuple of a migrator class, the from version, and the to version. A tuple is + * required to instantiate version numbers unless a default constructor is defined. + * @returns A new MigrationBuilder with the to version of the migrator as the current version. + */ + with< + TMigrator extends Migrator, + TFrom extends VersionFrom & TCurrent, + TTo extends VersionTo + >( + ...migrate: [new () => TMigrator] | [new (from: TFrom, to: TTo) => TMigrator, TFrom, TTo] + ): MigrationBuilder { + return this.addMigrator(migrate, "up"); + } + + /** Add a migrator to rollback on the MigrationBuilder's list of migrations. As with {@link with}, types of + * MigrationBuilder and Migrator must align. However, this time the migration is reversed so TCurrent of the + * MigrationBuilder must be equal to the to version of the migrator. Return as MigrationBuilder where TFrom + * is the from version of the migrator, so that the next migrator can be chained. + * + * @param migrate A migrator class or a tuple of a migrator class, the from version, and the to version. A tuple is + * required to instantiate version numbers unless a default constructor is defined. + * @returns A new MigrationBuilder with the from version of the migrator as the current version. + */ + rollback< + TMigrator extends Migrator, + TFrom extends VersionFrom, + TTo extends VersionTo & TCurrent + >( + ...migrate: [new () => TMigrator] | [new (from: TFrom, to: TTo) => TMigrator, TTo, TFrom] + ): MigrationBuilder { + if (migrate.length === 3) { + migrate = [migrate[0], migrate[2], migrate[1]]; + } + return this.addMigrator(migrate, "down"); + } + + /** Execute the migrations as defined in the MigrationBuilder's migrator buffer */ + migrate(helper: MigrationHelper): Promise { + return this.migrations.reduce( + (promise, migrator) => + promise.then(async () => { + await this.runMigrator(migrator.migrator, helper, migrator.direction); + }), + Promise.resolve() + ); + } + + private addMigrator< + TMigrator extends Migrator, + TFrom extends VersionFrom & TCurrent, + TTo extends VersionTo + >( + migrate: [new () => TMigrator] | [new (from: TFrom, to: TTo) => TMigrator, TFrom, TTo], + direction: Direction = "up" + ) { + const newMigration = + migrate.length === 1 + ? { migrator: new migrate[0](), direction } + : { migrator: new migrate[0](migrate[1], migrate[2]), direction }; + + return new MigrationBuilder([...this.migrations, newMigration]); + } + + private async runMigrator( + migrator: Migrator, + helper: MigrationHelper, + direction: Direction + ): Promise { + const shouldMigrate = await migrator.shouldMigrate(helper, direction); + helper.info( + `Migrator ${migrator.constructor.name} (to version ${migrator.toVersion}) should migrate: ${shouldMigrate} - ${direction}` + ); + if (shouldMigrate) { + const method = direction === "up" ? migrator.migrate : migrator.rollback; + await method(helper); + helper.info( + `Migrator ${migrator.constructor.name} (to version ${migrator.toVersion}) migrated - ${direction}` + ); + await migrator.updateVersion(helper, direction); + helper.info( + `Migrator ${migrator.constructor.name} (to version ${migrator.toVersion}) updated version - ${direction}` + ); + } + } +} diff --git a/libs/common/src/state-migrations/migration-helper.spec.ts b/libs/common/src/state-migrations/migration-helper.spec.ts new file mode 100644 index 00000000000..5b8a0f2eb4f --- /dev/null +++ b/libs/common/src/state-migrations/migration-helper.spec.ts @@ -0,0 +1,84 @@ +import { MockProxy, mock } from "jest-mock-extended"; + +// eslint-disable-next-line import/no-restricted-paths -- Needed to print log messages +import { LogService } from "../platform/abstractions/log.service"; +// eslint-disable-next-line import/no-restricted-paths -- Needed to interface with storage locations +import { AbstractStorageService } from "../platform/abstractions/storage.service"; + +import { MigrationHelper } from "./migration-helper"; + +const exampleJSON = { + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + otherStuff: "otherStuff1", + }, + "23e61a5f-2ece-4f5e-b499-f0bc489482a9": { + otherStuff: "otherStuff2", + }, +}; + +describe("RemoveLegacyEtmKeyMigrator", () => { + let storage: MockProxy; + let logService: MockProxy; + let sut: MigrationHelper; + + beforeEach(() => { + logService = mock(); + storage = mock(); + storage.get.mockImplementation((key) => (exampleJSON as any)[key]); + + sut = new MigrationHelper(0, storage, logService); + }); + + describe("get", () => { + it("should delegate to storage.get", async () => { + await sut.get("key"); + expect(storage.get).toHaveBeenCalledWith("key"); + }); + }); + + describe("set", () => { + it("should delegate to storage.save", async () => { + await sut.set("key", "value"); + expect(storage.save).toHaveBeenCalledWith("key", "value"); + }); + }); + + describe("getAccounts", () => { + it("should return all accounts", async () => { + const accounts = await sut.getAccounts(); + expect(accounts).toEqual([ + { userId: "c493ed01-4e08-4e88-abc7-332f380ca760", account: { otherStuff: "otherStuff1" } }, + { userId: "23e61a5f-2ece-4f5e-b499-f0bc489482a9", account: { otherStuff: "otherStuff2" } }, + ]); + }); + + it("should handle missing authenticatedAccounts", async () => { + storage.get.mockImplementation((key) => + key === "authenticatedAccounts" ? undefined : (exampleJSON as any)[key] + ); + const accounts = await sut.getAccounts(); + expect(accounts).toEqual([]); + }); + }); +}); + +/** Helper to create well-mocked migration helpers in migration tests */ +export function mockMigrationHelper(storageJson: any): MockProxy { + const logService: MockProxy = mock(); + const storage: MockProxy = mock(); + storage.get.mockImplementation((key) => (storageJson as any)[key]); + storage.save.mockImplementation(async (key, value) => { + (storageJson as any)[key] = value; + }); + const helper = new MigrationHelper(0, storage, logService); + + const mockHelper = mock(); + mockHelper.get.mockImplementation((key) => helper.get(key)); + mockHelper.set.mockImplementation((key, value) => helper.set(key, value)); + mockHelper.getAccounts.mockImplementation(() => helper.getAccounts()); + return mockHelper; +} diff --git a/libs/common/src/state-migrations/migration-helper.ts b/libs/common/src/state-migrations/migration-helper.ts new file mode 100644 index 00000000000..a185aa69a99 --- /dev/null +++ b/libs/common/src/state-migrations/migration-helper.ts @@ -0,0 +1,37 @@ +// eslint-disable-next-line import/no-restricted-paths -- Needed to print log messages +import { LogService } from "../platform/abstractions/log.service"; +// eslint-disable-next-line import/no-restricted-paths -- Needed to interface with storage locations +import { AbstractStorageService } from "../platform/abstractions/storage.service"; + +export class MigrationHelper { + constructor( + public currentVersion: number, + private storageService: AbstractStorageService, + public logService: LogService + ) {} + + get(key: string): Promise { + return this.storageService.get(key); + } + + set(key: string, value: T): Promise { + this.logService.info(`Setting ${key}`); + return this.storageService.save(key, value); + } + + info(message: string): void { + this.logService.info(message); + } + + async getAccounts(): Promise< + { userId: string; account: ExpectedAccountType }[] + > { + const userIds = (await this.get("authenticatedAccounts")) ?? []; + return Promise.all( + userIds.map(async (userId) => ({ + userId, + account: await this.get(userId), + })) + ); + } +} diff --git a/libs/common/src/state-migrations/migrations/3-fix-premium.spec.ts b/libs/common/src/state-migrations/migrations/3-fix-premium.spec.ts new file mode 100644 index 00000000000..1ef910d4569 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/3-fix-premium.spec.ts @@ -0,0 +1,111 @@ +import { MockProxy } from "jest-mock-extended"; + +// eslint-disable-next-line import/no-restricted-paths -- Used for testing migration, which requires import +import { TokenService } from "../../auth/services/token.service"; +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { FixPremiumMigrator } from "./3-fix-premium"; + +function migrateExampleJSON() { + return { + global: { + stateVersion: 2, + otherStuff: "otherStuff1", + }, + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + profile: { + otherStuff: "otherStuff2", + hasPremiumPersonally: null as boolean, + }, + tokens: { + otherStuff: "otherStuff3", + accessToken: "accessToken", + }, + otherStuff: "otherStuff4", + }, + "23e61a5f-2ece-4f5e-b499-f0bc489482a9": { + profile: { + otherStuff: "otherStuff5", + hasPremiumPersonally: true, + }, + tokens: { + otherStuff: "otherStuff6", + accessToken: "accessToken", + }, + otherStuff: "otherStuff7", + }, + otherStuff: "otherStuff8", + }; +} + +jest.mock("../../auth/services/token.service", () => ({ + TokenService: { + decodeToken: jest.fn(), + }, +})); + +describe("FixPremiumMigrator", () => { + let helper: MockProxy; + let sut: FixPremiumMigrator; + const decodeTokenSpy = TokenService.decodeToken as jest.Mock; + + beforeEach(() => { + helper = mockMigrationHelper(migrateExampleJSON()); + sut = new FixPremiumMigrator(2, 3); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe("migrate", () => { + it("should migrate hasPremiumPersonally", async () => { + decodeTokenSpy.mockResolvedValueOnce({ premium: true }); + await sut.migrate(helper); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("c493ed01-4e08-4e88-abc7-332f380ca760", { + profile: { + otherStuff: "otherStuff2", + hasPremiumPersonally: true, + }, + tokens: { + otherStuff: "otherStuff3", + accessToken: "accessToken", + }, + otherStuff: "otherStuff4", + }); + }); + + it("should not migrate if decode throws", async () => { + decodeTokenSpy.mockRejectedValueOnce(new Error("test")); + await sut.migrate(helper); + + expect(helper.set).not.toHaveBeenCalled(); + }); + + it("should not migrate if decode returns null", async () => { + decodeTokenSpy.mockResolvedValueOnce(null); + await sut.migrate(helper); + + expect(helper.set).not.toHaveBeenCalled(); + }); + }); + + describe("updateVersion", () => { + it("should update version", async () => { + await sut.updateVersion(helper, "up"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 3, + otherStuff: "otherStuff1", + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/3-fix-premium.ts b/libs/common/src/state-migrations/migrations/3-fix-premium.ts new file mode 100644 index 00000000000..b6c69a99168 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/3-fix-premium.ts @@ -0,0 +1,48 @@ +// eslint-disable-next-line import/no-restricted-paths -- Used for token decoding, which are valid for days. We want the latest +import { TokenService } from "../../auth/services/token.service"; +import { MigrationHelper } from "../migration-helper"; +import { Migrator, IRREVERSIBLE, Direction } from "../migrator"; + +type ExpectedAccountType = { + profile?: { hasPremiumPersonally?: boolean }; + tokens?: { accessToken?: string }; +}; + +export class FixPremiumMigrator extends Migrator<2, 3> { + async migrate(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + + async function fixPremium(userId: string, account: ExpectedAccountType) { + if (account?.profile?.hasPremiumPersonally === null && account.tokens?.accessToken != null) { + let decodedToken: { premium: boolean }; + try { + decodedToken = await TokenService.decodeToken(account.tokens.accessToken); + } catch { + return; + } + + if (decodedToken?.premium == null) { + return; + } + + account.profile.hasPremiumPersonally = decodedToken?.premium; + return helper.set(userId, account); + } + } + + await Promise.all(accounts.map(({ userId, account }) => fixPremium(userId, account))); + } + + rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } + + // Override is necessary because default implementation assumes `stateVersion` at the root, but for this version + // it is nested inside a global object. + override async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + const global: Record = (await helper.get("global")) || {}; + await helper.set("global", { ...global, stateVersion: endVersion }); + } +} diff --git a/libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.spec.ts b/libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.spec.ts new file mode 100644 index 00000000000..1701762118d --- /dev/null +++ b/libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.spec.ts @@ -0,0 +1,75 @@ +import { MockProxy } from "jest-mock-extended"; + +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { RemoveEverBeenUnlockedMigrator } from "./4-remove-ever-been-unlocked"; + +function migrateExampleJSON() { + return { + global: { + stateVersion: 3, + otherStuff: "otherStuff1", + }, + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + profile: { + otherStuff: "otherStuff2", + everBeenUnlocked: true, + }, + otherStuff: "otherStuff3", + }, + "23e61a5f-2ece-4f5e-b499-f0bc489482a9": { + profile: { + otherStuff: "otherStuff4", + everBeenUnlocked: false, + }, + otherStuff: "otherStuff5", + }, + otherStuff: "otherStuff6", + }; +} + +describe("RemoveEverBeenUnlockedMigrator", () => { + let helper: MockProxy; + let sut: RemoveEverBeenUnlockedMigrator; + + beforeEach(() => { + helper = mockMigrationHelper(migrateExampleJSON()); + sut = new RemoveEverBeenUnlockedMigrator(3, 4); + }); + + describe("migrate", () => { + it("should remove everBeenUnlocked from profile", async () => { + await sut.migrate(helper); + expect(helper.set).toHaveBeenCalledTimes(2); + expect(helper.set).toHaveBeenCalledWith("c493ed01-4e08-4e88-abc7-332f380ca760", { + profile: { + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }); + expect(helper.set).toHaveBeenCalledWith("23e61a5f-2ece-4f5e-b499-f0bc489482a9", { + profile: { + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }); + }); + }); + + describe("updateVersion", () => { + it("should update version up", async () => { + await sut.updateVersion(helper, "up"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 4, + otherStuff: "otherStuff1", + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.ts b/libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.ts new file mode 100644 index 00000000000..cfa45958d06 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/4-remove-ever-been-unlocked.ts @@ -0,0 +1,32 @@ +import { MigrationHelper } from "../migration-helper"; +import { Direction, IRREVERSIBLE, Migrator } from "../migrator"; + +type ExpectedAccountType = { profile?: { everBeenUnlocked?: boolean } }; + +export class RemoveEverBeenUnlockedMigrator extends Migrator<3, 4> { + async migrate(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + + async function removeEverBeenUnlocked(userId: string, account: ExpectedAccountType) { + if (account?.profile?.everBeenUnlocked != null) { + delete account.profile.everBeenUnlocked; + return helper.set(userId, account); + } + } + + Promise.all(accounts.map(({ userId, account }) => removeEverBeenUnlocked(userId, account))); + } + + rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } + + // Override is necessary because default implementation assumes `stateVersion` at the root, but for this version + // it is nested inside a global object. + override async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + const global: { stateVersion: number } = (await helper.get("global")) || ({} as any); + await helper.set("global", { ...global, stateVersion: endVersion }); + } +} diff --git a/libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.spec.ts b/libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.spec.ts new file mode 100644 index 00000000000..028a0b879b1 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.spec.ts @@ -0,0 +1,141 @@ +import { MockProxy } from "jest-mock-extended"; + +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { AddKeyTypeToOrgKeysMigrator } from "./5-add-key-type-to-org-keys"; + +function migrateExampleJSON() { + return { + global: { + stateVersion: 4, + otherStuff: "otherStuff1", + }, + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + keys: { + organizationKeys: { + encrypted: { + orgOneId: "orgOneEncKey", + orgTwoId: "orgTwoEncKey", + }, + }, + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }, + }; +} + +function rollbackExampleJSON() { + return { + global: { + stateVersion: 5, + otherStuff: "otherStuff1", + }, + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + keys: { + organizationKeys: { + encrypted: { + orgOneId: { + type: "organization", + key: "orgOneEncKey", + }, + orgTwoId: { + type: "organization", + key: "orgTwoEncKey", + }, + }, + }, + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }, + }; +} + +describe("AddKeyTypeToOrgKeysMigrator", () => { + let helper: MockProxy; + let sut: AddKeyTypeToOrgKeysMigrator; + + describe("migrate", () => { + beforeEach(() => { + helper = mockMigrationHelper(migrateExampleJSON()); + sut = new AddKeyTypeToOrgKeysMigrator(4, 5); + }); + + it("should add organization type to organization keys", async () => { + await sut.migrate(helper); + + expect(helper.set).toHaveBeenCalledWith("c493ed01-4e08-4e88-abc7-332f380ca760", { + keys: { + organizationKeys: { + encrypted: { + orgOneId: { + type: "organization", + key: "orgOneEncKey", + }, + orgTwoId: { + type: "organization", + key: "orgTwoEncKey", + }, + }, + }, + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }); + }); + + it("should update version", async () => { + await sut.updateVersion(helper, "up"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 5, + otherStuff: "otherStuff1", + }); + }); + }); + + describe("rollback", () => { + beforeEach(() => { + helper = mockMigrationHelper(rollbackExampleJSON()); + sut = new AddKeyTypeToOrgKeysMigrator(4, 5); + }); + + it("should remove type from orgainzation keys", async () => { + await sut.rollback(helper); + + expect(helper.set).toHaveBeenCalledWith("c493ed01-4e08-4e88-abc7-332f380ca760", { + keys: { + organizationKeys: { + encrypted: { + orgOneId: "orgOneEncKey", + orgTwoId: "orgTwoEncKey", + }, + }, + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }); + }); + + it("should update version down", async () => { + await sut.updateVersion(helper, "down"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 4, + otherStuff: "otherStuff1", + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.ts b/libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.ts new file mode 100644 index 00000000000..ab1550c52e3 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/5-add-key-type-to-org-keys.ts @@ -0,0 +1,67 @@ +import { MigrationHelper } from "../migration-helper"; +import { Direction, Migrator } from "../migrator"; + +type ExpectedAccountType = { keys?: { organizationKeys?: { encrypted: Record } } }; +type NewAccountType = { + keys?: { + organizationKeys?: { encrypted: Record }; + }; +}; + +export class AddKeyTypeToOrgKeysMigrator extends Migrator<4, 5> { + async migrate(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + + async function updateOrgKey(userId: string, account: ExpectedAccountType) { + const encryptedOrgKeys = account?.keys?.organizationKeys?.encrypted; + if (encryptedOrgKeys == null) { + return; + } + + const newOrgKeys: Record = {}; + + Object.entries(encryptedOrgKeys).forEach(([orgId, encKey]) => { + newOrgKeys[orgId] = { + type: "organization", + key: encKey, + }; + }); + (account as any).keys.organizationKeys.encrypted = newOrgKeys; + + await helper.set(userId, account); + } + + Promise.all(accounts.map(({ userId, account }) => updateOrgKey(userId, account))); + } + + async rollback(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + + async function updateOrgKey(userId: string, account: NewAccountType) { + const encryptedOrgKeys = account?.keys?.organizationKeys?.encrypted; + if (encryptedOrgKeys == null) { + return; + } + + const newOrgKeys: Record = {}; + + Object.entries(encryptedOrgKeys).forEach(([orgId, encKey]) => { + newOrgKeys[orgId] = encKey.key; + }); + (account as any).keys.organizationKeys.encrypted = newOrgKeys; + + await helper.set(userId, account); + } + + Promise.all(accounts.map(async ({ userId, account }) => updateOrgKey(userId, account))); + } + + // Override is necessary because default implementation assumes `stateVersion` at the root, but for this version + // it is nested inside a global object. + override async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + const global: { stateVersion: number } = (await helper.get("global")) || ({} as any); + await helper.set("global", { ...global, stateVersion: endVersion }); + } +} diff --git a/libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.spec.ts b/libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.spec.ts new file mode 100644 index 00000000000..bc7b862f6cf --- /dev/null +++ b/libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.spec.ts @@ -0,0 +1,80 @@ +import { MockProxy } from "jest-mock-extended"; + +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { RemoveLegacyEtmKeyMigrator } from "./6-remove-legacy-etm-key"; + +function exampleJSON() { + return { + global: { + stateVersion: 5, + otherStuff: "otherStuff1", + }, + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + "fd005ea6-a16a-45ef-ba4a-a194269bfd73", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + keys: { + legacyEtmKey: "legacyEtmKey", + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }, + "23e61a5f-2ece-4f5e-b499-f0bc489482a9": { + keys: { + legacyEtmKey: "legacyEtmKey", + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }, + }; +} + +describe("RemoveLegacyEtmKeyMigrator", () => { + let helper: MockProxy; + let sut: RemoveLegacyEtmKeyMigrator; + + beforeEach(() => { + helper = mockMigrationHelper(exampleJSON()); + sut = new RemoveLegacyEtmKeyMigrator(5, 6); + }); + + describe("migrate", () => { + it("should remove legacyEtmKey from all accounts", async () => { + await sut.migrate(helper); + expect(helper.set).toHaveBeenCalledWith("c493ed01-4e08-4e88-abc7-332f380ca760", { + keys: { + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }); + expect(helper.set).toHaveBeenCalledWith("23e61a5f-2ece-4f5e-b499-f0bc489482a9", { + keys: { + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }); + }); + }); + + describe("rollback", () => { + it("should throw", async () => { + await expect(sut.rollback(helper)).rejects.toThrow(); + }); + }); + + describe("updateVersion", () => { + it("should update version up", async () => { + await sut.updateVersion(helper, "up"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 6, + otherStuff: "otherStuff1", + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.ts b/libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.ts new file mode 100644 index 00000000000..2a06916ea33 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/6-remove-legacy-etm-key.ts @@ -0,0 +1,32 @@ +import { MigrationHelper } from "../migration-helper"; +import { Direction, IRREVERSIBLE, Migrator } from "../migrator"; + +type ExpectedAccountType = { keys?: { legacyEtmKey?: string } }; + +export class RemoveLegacyEtmKeyMigrator extends Migrator<5, 6> { + async migrate(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + + async function updateAccount(userId: string, account: ExpectedAccountType) { + if (account?.keys?.legacyEtmKey) { + delete account.keys.legacyEtmKey; + await helper.set(userId, account); + } + } + + await Promise.all(accounts.map(({ userId, account }) => updateAccount(userId, account))); + } + + async rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } + + // Override is necessary because default implementation assumes `stateVersion` at the root, but for this version + // it is nested inside a global object. + override async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + const global: { stateVersion: number } = (await helper.get("global")) || ({} as any); + await helper.set("global", { ...global, stateVersion: endVersion }); + } +} diff --git a/libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.spec.ts b/libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.spec.ts new file mode 100644 index 00000000000..fe73f8a9bc4 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.spec.ts @@ -0,0 +1,102 @@ +import { MockProxy, any, matches } from "jest-mock-extended"; + +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { MoveBiometricAutoPromptToAccount } from "./7-move-biometric-auto-prompt-to-account"; + +function exampleJSON() { + return { + global: { + stateVersion: 6, + noAutoPromptBiometrics: true, + otherStuff: "otherStuff1", + }, + authenticatedAccounts: [ + "c493ed01-4e08-4e88-abc7-332f380ca760", + "23e61a5f-2ece-4f5e-b499-f0bc489482a9", + "fd005ea6-a16a-45ef-ba4a-a194269bfd73", + ], + "c493ed01-4e08-4e88-abc7-332f380ca760": { + settings: { + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }, + "23e61a5f-2ece-4f5e-b499-f0bc489482a9": { + settings: { + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }, + }; +} + +describe("RemoveLegacyEtmKeyMigrator", () => { + let helper: MockProxy; + let sut: MoveBiometricAutoPromptToAccount; + + beforeEach(() => { + helper = mockMigrationHelper(exampleJSON()); + sut = new MoveBiometricAutoPromptToAccount(6, 7); + }); + + describe("migrate", () => { + it("should remove noAutoPromptBiometrics from global", async () => { + await sut.migrate(helper); + expect(helper.set).toHaveBeenCalledWith("global", { + otherStuff: "otherStuff1", + stateVersion: 6, + }); + }); + + it("should set disableAutoBiometricsPrompt to true on all accounts", async () => { + await sut.migrate(helper); + expect(helper.set).toHaveBeenCalledWith("c493ed01-4e08-4e88-abc7-332f380ca760", { + settings: { + disableAutoBiometricsPrompt: true, + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }); + expect(helper.set).toHaveBeenCalledWith("23e61a5f-2ece-4f5e-b499-f0bc489482a9", { + settings: { + disableAutoBiometricsPrompt: true, + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }); + }); + + it("should not set disableAutoBiometricsPrompt to true on accounts if noAutoPromptBiometrics is false", async () => { + const json = exampleJSON(); + json.global.noAutoPromptBiometrics = false; + helper = mockMigrationHelper(json); + await sut.migrate(helper); + expect(helper.set).not.toHaveBeenCalledWith( + matches((s) => s != "global"), + any() + ); + }); + }); + + describe("rollback", () => { + it("should throw", async () => { + await expect(sut.rollback(helper)).rejects.toThrow(); + }); + }); + + describe("updateVersion", () => { + it("should update version up", async () => { + await sut.updateVersion(helper, "up"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith( + "global", + Object.assign({}, exampleJSON().global, { + stateVersion: 7, + }) + ); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.ts b/libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.ts new file mode 100644 index 00000000000..0ac065d60c1 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/7-move-biometric-auto-prompt-to-account.ts @@ -0,0 +1,45 @@ +import { MigrationHelper } from "../migration-helper"; +import { Direction, IRREVERSIBLE, Migrator } from "../migrator"; + +type ExpectedAccountType = { settings?: { disableAutoBiometricsPrompt?: boolean } }; + +export class MoveBiometricAutoPromptToAccount extends Migrator<6, 7> { + async migrate(helper: MigrationHelper): Promise { + const global = await helper.get<{ noAutoPromptBiometrics?: boolean }>("global"); + const noAutoPromptBiometrics = global?.noAutoPromptBiometrics ?? false; + + const accounts = await helper.getAccounts(); + async function updateAccount(userId: string, account: ExpectedAccountType) { + if (account == null) { + return; + } + + if (noAutoPromptBiometrics) { + account.settings = Object.assign(account?.settings ?? {}, { + disableAutoBiometricsPrompt: true, + }); + await helper.set(userId, account); + } + } + + delete global.noAutoPromptBiometrics; + + await Promise.all([ + ...accounts.map(({ userId, account }) => updateAccount(userId, account)), + helper.set("global", global), + ]); + } + + async rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } + + // Override is necessary because default implementation assumes `stateVersion` at the root, but for this version + // it is nested inside a global object. + override async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + const global: { stateVersion: number } = (await helper.get("global")) || ({} as any); + await helper.set("global", { ...global, stateVersion: endVersion }); + } +} diff --git a/libs/common/src/state-migrations/migrations/8-move-state-version.spec.ts b/libs/common/src/state-migrations/migrations/8-move-state-version.spec.ts new file mode 100644 index 00000000000..8c84fd642ea --- /dev/null +++ b/libs/common/src/state-migrations/migrations/8-move-state-version.spec.ts @@ -0,0 +1,90 @@ +import { MockProxy } from "jest-mock-extended"; + +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { MoveStateVersionMigrator } from "./8-move-state-version"; + +function migrateExampleJSON() { + return { + global: { + stateVersion: 6, + otherStuff: "otherStuff1", + }, + otherStuff: "otherStuff2", + }; +} + +function rollbackExampleJSON() { + return { + global: { + otherStuff: "otherStuff1", + }, + stateVersion: 7, + otherStuff: "otherStuff2", + }; +} + +describe("moveStateVersion", () => { + let helper: MockProxy; + let sut: MoveStateVersionMigrator; + + describe("migrate", () => { + beforeEach(() => { + helper = mockMigrationHelper(migrateExampleJSON()); + sut = new MoveStateVersionMigrator(7, 8); + }); + + it("should move state version to root", async () => { + await sut.migrate(helper); + expect(helper.set).toHaveBeenCalledWith("stateVersion", 6); + }); + + it("should remove state version from global", async () => { + await sut.migrate(helper); + expect(helper.set).toHaveBeenCalledWith("global", { + otherStuff: "otherStuff1", + }); + }); + + it("should throw if state version not found", async () => { + helper.get.mockReturnValue({ otherStuff: "otherStuff1" } as any); + await expect(sut.migrate(helper)).rejects.toThrow( + "Migration failed, state version not found" + ); + }); + + it("should update version up", async () => { + await sut.updateVersion(helper, "up"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("stateVersion", 8); + }); + }); + + describe("rollback", () => { + beforeEach(() => { + helper = mockMigrationHelper(rollbackExampleJSON()); + sut = new MoveStateVersionMigrator(7, 8); + }); + + it("should move state version to global", async () => { + await sut.rollback(helper); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 7, + otherStuff: "otherStuff1", + }); + expect(helper.set).toHaveBeenCalledWith("stateVersion", undefined); + }); + + it("should update version down", async () => { + await sut.updateVersion(helper, "down"); + + expect(helper.set).toHaveBeenCalledTimes(1); + expect(helper.set).toHaveBeenCalledWith("global", { + stateVersion: 7, + otherStuff: "otherStuff1", + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/8-move-state-version.ts b/libs/common/src/state-migrations/migrations/8-move-state-version.ts new file mode 100644 index 00000000000..cbcdf423843 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/8-move-state-version.ts @@ -0,0 +1,37 @@ +import { JsonObject } from "type-fest"; + +import { MigrationHelper } from "../migration-helper"; +import { Direction, Migrator } from "../migrator"; + +export class MoveStateVersionMigrator extends Migrator<7, 8> { + async migrate(helper: MigrationHelper): Promise { + const global = await helper.get<{ stateVersion: number }>("global"); + if (global.stateVersion) { + await helper.set("stateVersion", global.stateVersion); + delete global.stateVersion; + await helper.set("global", global); + } else { + throw new Error("Migration failed, state version not found"); + } + } + + async rollback(helper: MigrationHelper): Promise { + const version = await helper.get("stateVersion"); + const global = await helper.get("global"); + await helper.set("global", { ...global, stateVersion: version }); + await helper.set("stateVersion", undefined); + } + + // Override is necessary because default implementation assumes `stateVersion` at the root, but this migration moves + // it from a `global` object to root.This makes for unique rollback versioning. + override async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + if (direction === "up") { + await helper.set("stateVersion", endVersion); + } else { + const global: { stateVersion: number } = (await helper.get("global")) || ({} as any); + await helper.set("global", { ...global, stateVersion: endVersion }); + } + } +} diff --git a/libs/common/src/state-migrations/migrations/min-version.spec.ts b/libs/common/src/state-migrations/migrations/min-version.spec.ts new file mode 100644 index 00000000000..26e106c19a9 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/min-version.spec.ts @@ -0,0 +1,29 @@ +import { MockProxy } from "jest-mock-extended"; + +import { MIN_VERSION } from "../migrate"; +import { MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { MinVersionMigrator } from "./min-version"; + +describe("MinVersionMigrator", () => { + let helper: MockProxy; + let sut: MinVersionMigrator; + + beforeEach(() => { + helper = mockMigrationHelper(null); + sut = new MinVersionMigrator(); + }); + + describe("shouldMigrate", () => { + it("should return true if current version is less than min version", async () => { + helper.currentVersion = MIN_VERSION - 1; + expect(await sut.shouldMigrate(helper)).toBe(true); + }); + + it("should return false if current version is greater than min version", async () => { + helper.currentVersion = MIN_VERSION + 1; + expect(await sut.shouldMigrate(helper)).toBe(false); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/min-version.ts b/libs/common/src/state-migrations/migrations/min-version.ts new file mode 100644 index 00000000000..a417cc51a3c --- /dev/null +++ b/libs/common/src/state-migrations/migrations/min-version.ts @@ -0,0 +1,26 @@ +import { MinVersion, MIN_VERSION } from "../migrate"; +import { MigrationHelper } from "../migration-helper"; +import { IRREVERSIBLE, Migrator } from "../migrator"; + +export function minVersionError(current: number) { + return `Your local data is too old to be migrated. Your current state version is ${current}, but minimum version is ${MIN_VERSION}.`; +} + +export class MinVersionMigrator extends Migrator<0, MinVersion> { + constructor() { + super(0, MIN_VERSION); + } + + // Overrides the default implementation to catch any version that may be passed in. + override shouldMigrate(helper: MigrationHelper): Promise { + return Promise.resolve(helper.currentVersion < MIN_VERSION); + } + async migrate(helper: MigrationHelper): Promise { + if (helper.currentVersion < MIN_VERSION) { + throw new Error(minVersionError(helper.currentVersion)); + } + } + async rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } +} diff --git a/libs/common/src/state-migrations/migrator.spec.ts b/libs/common/src/state-migrations/migrator.spec.ts new file mode 100644 index 00000000000..3abaa277273 --- /dev/null +++ b/libs/common/src/state-migrations/migrator.spec.ts @@ -0,0 +1,75 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +// eslint-disable-next-line import/no-restricted-paths -- Needed to print log messages +import { LogService } from "../platform/abstractions/log.service"; +// eslint-disable-next-line import/no-restricted-paths -- Needed to interface with storage locations +import { AbstractStorageService } from "../platform/abstractions/storage.service"; + +import { MigrationHelper } from "./migration-helper"; +import { Migrator } from "./migrator"; + +describe("migrator default methods", () => { + class TestMigrator extends Migrator<0, 1> { + async migrate(helper: MigrationHelper): Promise { + await helper.set("test", "test"); + } + async rollback(helper: MigrationHelper): Promise { + await helper.set("test", "rollback"); + } + } + + let storage: MockProxy; + let logService: MockProxy; + let helper: MigrationHelper; + let sut: TestMigrator; + + beforeEach(() => { + storage = mock(); + logService = mock(); + helper = new MigrationHelper(0, storage, logService); + sut = new TestMigrator(0, 1); + }); + + describe("shouldMigrate", () => { + describe("up", () => { + it("should return true if the current version equals the from version", async () => { + expect(await sut.shouldMigrate(helper, "up")).toBe(true); + }); + + it("should return false if the current version does not equal the from version", async () => { + helper.currentVersion = 1; + expect(await sut.shouldMigrate(helper, "up")).toBe(false); + }); + }); + + describe("down", () => { + it("should return true if the current version equals the to version", async () => { + helper.currentVersion = 1; + expect(await sut.shouldMigrate(helper, "down")).toBe(true); + }); + + it("should return false if the current version does not equal the to version", async () => { + expect(await sut.shouldMigrate(helper, "down")).toBe(false); + }); + }); + }); + + describe("updateVersion", () => { + describe("up", () => { + it("should update the version", async () => { + await sut.updateVersion(helper, "up"); + expect(storage.save).toBeCalledWith("stateVersion", 1); + expect(helper.currentVersion).toBe(1); + }); + }); + + describe("down", () => { + it("should update the version", async () => { + helper.currentVersion = 1; + await sut.updateVersion(helper, "down"); + expect(storage.save).toBeCalledWith("stateVersion", 0); + expect(helper.currentVersion).toBe(0); + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrator.ts b/libs/common/src/state-migrations/migrator.ts new file mode 100644 index 00000000000..aba81372d49 --- /dev/null +++ b/libs/common/src/state-migrations/migrator.ts @@ -0,0 +1,40 @@ +import { NonNegativeInteger } from "type-fest"; + +import { MigrationHelper } from "./migration-helper"; + +export const IRREVERSIBLE = new Error("Irreversible migration"); + +export type VersionFrom = T extends Migrator + ? TFrom extends NonNegativeInteger + ? TFrom + : never + : never; +export type VersionTo = T extends Migrator + ? TTo extends NonNegativeInteger + ? TTo + : never + : never; +export type Direction = "up" | "down"; + +export abstract class Migrator { + constructor(public fromVersion: TFrom, public toVersion: TTo) { + if (fromVersion == null || toVersion == null) { + throw new Error("Invalid migration"); + } + if (fromVersion > toVersion) { + throw new Error("Invalid migration"); + } + } + + shouldMigrate(helper: MigrationHelper, direction: Direction): Promise { + const startVersion = direction === "up" ? this.fromVersion : this.toVersion; + return Promise.resolve(helper.currentVersion === startVersion); + } + abstract migrate(helper: MigrationHelper): Promise; + abstract rollback(helper: MigrationHelper): Promise; + async updateVersion(helper: MigrationHelper, direction: Direction): Promise { + const endVersion = direction === "up" ? this.toVersion : this.fromVersion; + helper.currentVersion = endVersion; + await helper.set("stateVersion", endVersion); + } +} From f61793b10b1b026c23a169bcba26eaac12c2d313 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Wed, 30 Aug 2023 15:08:41 -0400 Subject: [PATCH 076/135] [PM-3588] Close duplicate single-action windows (#6091) * close duplicate single-action windows * Use longform conditional Co-authored-by: Cesar Gonzalez --------- Co-authored-by: Cesar Gonzalez --- .../platform/popup/browser-popout-window.service.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/browser/src/platform/popup/browser-popout-window.service.ts b/apps/browser/src/platform/popup/browser-popout-window.service.ts index 95be15cc20d..ee03e3a2ec4 100644 --- a/apps/browser/src/platform/popup/browser-popout-window.service.ts +++ b/apps/browser/src/platform/popup/browser-popout-window.service.ts @@ -12,7 +12,6 @@ class BrowserPopoutWindowService implements BrowserPopupWindowServiceInterface { }; async openUnlockPrompt(senderWindowId: number) { - await this.closeUnlockPrompt(); await this.openSingleActionPopout( senderWindowId, "popup/index.html?uilocation=popout", @@ -36,8 +35,6 @@ class BrowserPopoutWindowService implements BrowserPopupWindowServiceInterface { action: string; } ) { - await this.closePasswordRepromptPrompt(); - const promptWindowPath = "popup/index.html#/view-cipher" + "?uilocation=popout" + @@ -73,18 +70,16 @@ class BrowserPopoutWindowService implements BrowserPopupWindowServiceInterface { const popupWindow = await BrowserApi.createWindow(windowOptions); - if (!singleActionPopoutKey) { - return; - } + await this.closeSingleActionPopout(singleActionPopoutKey); this.singleActionPopoutTabIds[singleActionPopoutKey] = popupWindow?.tabs[0].id; } private async closeSingleActionPopout(popoutKey: string) { const tabId = this.singleActionPopoutTabIds[popoutKey]; - if (!tabId) { - return; + + if (tabId) { + await BrowserApi.removeTab(tabId); } - await BrowserApi.removeTab(tabId); this.singleActionPopoutTabIds[popoutKey] = null; } } From 066056bd4564f76e1731b3018f585a82fc196554 Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Wed, 30 Aug 2023 21:27:26 +0100 Subject: [PATCH 077/135] [PM-3226] Adding session to ReferenceEventRequest (#6114) * Adding session to ReferenceEventRequest * Added comment to regex --- .../auth/trial-initiation/trial-initiation.component.ts | 7 +++++++ libs/common/src/models/request/reference-event.request.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/apps/web/src/app/auth/trial-initiation/trial-initiation.component.ts b/apps/web/src/app/auth/trial-initiation/trial-initiation.component.ts index e18c7548e03..183b57a90c6 100644 --- a/apps/web/src/app/auth/trial-initiation/trial-initiation.component.ts +++ b/apps/web/src/app/auth/trial-initiation/trial-initiation.component.ts @@ -94,6 +94,13 @@ export class TrialInitiationComponent implements OnInit, OnDestroy { if (this.referenceData.id === "") { this.referenceData.id = null; + } else { + // Matches "_ga_QBRN562QQQ=value1.value2.session" and captures values and session. + const regex = /_ga_QBRN562QQQ=([^.]+)\.([^.]+)\.(\d+)/; + const match = document.cookie.match(regex); + if (match) { + this.referenceData.session = match[3]; + } } } diff --git a/libs/common/src/models/request/reference-event.request.ts b/libs/common/src/models/request/reference-event.request.ts index 7a0b535a126..73a2532743a 100644 --- a/libs/common/src/models/request/reference-event.request.ts +++ b/libs/common/src/models/request/reference-event.request.ts @@ -1,5 +1,6 @@ export class ReferenceEventRequest { id: string; + session: string; layout: string; flow: string; } From 8669f81c1baac58cc16f7138498ea9fe04ce9fd0 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:25:17 -0700 Subject: [PATCH 078/135] Make WebAuthn a Free Method (#6079) * remove webauthn premium badge * update premium two-stop options text for web clients --- apps/browser/src/_locales/en/messages.json | 4 ++-- apps/browser/src/popup/settings/premium.component.html | 2 +- apps/desktop/src/locales/en/messages.json | 4 ++-- apps/desktop/src/vault/app/accounts/premium.component.html | 2 +- apps/web/src/app/vault/settings/premium.component.html | 2 +- apps/web/src/locales/en/messages.json | 4 ++-- libs/common/src/auth/services/two-factor.service.ts | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/popup/settings/premium.component.html b/apps/browser/src/popup/settings/premium.component.html index a9a569ec176..2727ee405b9 100644 --- a/apps/browser/src/popup/settings/premium.component.html +++ b/apps/browser/src/popup/settings/premium.component.html @@ -22,7 +22,7 @@
  • - {{ "ppremiumSignUpTwoStep" | i18n }} + {{ "premiumSignUpTwoStepOptions" | i18n }}
  • diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index dd2d38e38b4..dbcb70d5e0c 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/vault/app/accounts/premium.component.html b/apps/desktop/src/vault/app/accounts/premium.component.html index b91a021f9cc..b3a847dce12 100644 --- a/apps/desktop/src/vault/app/accounts/premium.component.html +++ b/apps/desktop/src/vault/app/accounts/premium.component.html @@ -17,7 +17,7 @@
  • - {{ "premiumSignUpTwoStep" | i18n }} + {{ "premiumSignUpTwoStepOptions" | i18n }}
  • diff --git a/apps/web/src/app/vault/settings/premium.component.html b/apps/web/src/app/vault/settings/premium.component.html index b47bfc1be2b..2c8c9d6146a 100644 --- a/apps/web/src/app/vault/settings/premium.component.html +++ b/apps/web/src/app/vault/settings/premium.component.html @@ -21,7 +21,7 @@
  • - {{ "premiumSignUpTwoStep" | i18n }} + {{ "premiumSignUpTwoStepOptions" | i18n }}
  • diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 1567bc65895..2e747f3cefd 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" diff --git a/libs/common/src/auth/services/two-factor.service.ts b/libs/common/src/auth/services/two-factor.service.ts index b391b7d39e1..71687b675ee 100644 --- a/libs/common/src/auth/services/two-factor.service.ts +++ b/libs/common/src/auth/services/two-factor.service.ts @@ -55,7 +55,7 @@ export const TwoFactorProviders: Partial Date: Thu, 31 Aug 2023 16:53:51 -0400 Subject: [PATCH 079/135] PM-3623 - VaultTimeoutInputComp - resolve double value changes emission issue which was causing double dialogs to be shown when the user chose never as a vault timeout option by not emitting the setting of the custom hours & min values based on the user's non-custom timeout choice. + Add interface for form value to replace any. (#6098) --- .../settings/vault-timeout-input.component.ts | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/libs/angular/src/components/settings/vault-timeout-input.component.ts b/libs/angular/src/components/settings/vault-timeout-input.component.ts index e01a01fdd1d..d23fbe29367 100644 --- a/libs/angular/src/components/settings/vault-timeout-input.component.ts +++ b/libs/angular/src/components/settings/vault-timeout-input.component.ts @@ -15,6 +15,14 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +interface VaultTimeoutFormValue { + vaultTimeout: number | null; + custom: { + hours: number | null; + minutes: number | null; + }; +} + @Directive() export class VaultTimeoutInputComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy, OnChanges @@ -70,11 +78,13 @@ export class VaultTimeoutInputComponent this.applyVaultTimeoutPolicy(); }); - this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => { - if (this.onChange) { - this.onChange(this.getVaultTimeout(value)); - } - }); + this.form.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe((value: VaultTimeoutFormValue) => { + if (this.onChange) { + this.onChange(this.getVaultTimeout(value)); + } + }); // Assign the current value to the custom fields // so that if the user goes from a numeric value to custom @@ -87,12 +97,19 @@ export class VaultTimeoutInputComponent ) .subscribe((value) => { const current = Math.max(value, 0); - this.form.patchValue({ - custom: { - hours: Math.floor(current / 60), - minutes: current % 60, + + // This cannot emit an event b/c it would cause form.valueChanges to fire again + // and we are already handling that above so just silently update + // custom fields when vaultTimeout changes to a non-custom value + this.form.patchValue( + { + custom: { + hours: Math.floor(current / 60), + minutes: current % 60, + }, }, - }); + { emitEvent: false } + ); }); this.canLockVault$ = this.vaultTimeoutSettingsService @@ -116,7 +133,7 @@ export class VaultTimeoutInputComponent } } - getVaultTimeout(value: any) { + getVaultTimeout(value: VaultTimeoutFormValue) { if (value.vaultTimeout !== VaultTimeoutInputComponent.CUSTOM_VALUE) { return value.vaultTimeout; } From ac1c7f9c8f8ad0979cf0a00a28212366ef0e9b00 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Thu, 31 Aug 2023 18:06:47 -0400 Subject: [PATCH 080/135] Auth - LoginComp - Focus logic bugfix - add null check to avoid error as focusInput was being called prematurely in some scenarios - confirmed the focus logic still works (#6095) --- apps/desktop/src/auth/login/login.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/auth/login/login.component.ts b/apps/desktop/src/auth/login/login.component.ts index 45b330f6dae..d1c6c88d14b 100644 --- a/apps/desktop/src/auth/login/login.component.ts +++ b/apps/desktop/src/auth/login/login.component.ts @@ -182,6 +182,6 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy { private focusInput() { const email = this.loggedEmail; - document.getElementById(email == null || email === "" ? "email" : "masterPassword").focus(); + document.getElementById(email == null || email === "" ? "email" : "masterPassword")?.focus(); } } From 1d7360bfdd36248f19aa048bd1012353e2c6f3b9 Mon Sep 17 00:00:00 2001 From: Dave Nicolson Date: Fri, 1 Sep 2023 12:06:49 +0200 Subject: [PATCH 081/135] [PS-1438] Prevent new line feed when selecting and copying passwords (#3460) * Prevent new line feed when selecting password * Prevent new line feed when copying password --- apps/desktop/src/scss/misc.scss | 1 - libs/angular/src/directives/copy-text.directive.ts | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss index a72b7754c13..8ed6a9b54be 100644 --- a/apps/desktop/src/scss/misc.scss +++ b/apps/desktop/src/scss/misc.scss @@ -561,7 +561,6 @@ h2, h3, label, a, -button, p, img, .box-header, diff --git a/libs/angular/src/directives/copy-text.directive.ts b/libs/angular/src/directives/copy-text.directive.ts index e3298c214c0..b595085e43b 100644 --- a/libs/angular/src/directives/copy-text.directive.ts +++ b/libs/angular/src/directives/copy-text.directive.ts @@ -1,5 +1,6 @@ import { Directive, ElementRef, HostListener, Input } from "@angular/core"; +import { ClientType } from "@bitwarden/common/enums"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @Directive({ @@ -15,6 +16,9 @@ export class CopyTextDirective { return; } - this.platformUtilsService.copyToClipboard(this.copyText, { window: window }); + const timeout = this.platformUtilsService.getClientType() === ClientType.Desktop ? 100 : 0; + setTimeout(() => { + this.platformUtilsService.copyToClipboard(this.copyText, { window: window }); + }, timeout); } } From 7d04974bd4cb2d37d36b028bd26828cd550568c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:43:17 +0000 Subject: [PATCH 082/135] Autosync the updated translations (#6167) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 4 +- apps/browser/src/_locales/az/messages.json | 4 +- apps/browser/src/_locales/be/messages.json | 4 +- apps/browser/src/_locales/bg/messages.json | 4 +- apps/browser/src/_locales/bn/messages.json | 4 +- apps/browser/src/_locales/bs/messages.json | 4 +- apps/browser/src/_locales/ca/messages.json | 4 +- apps/browser/src/_locales/cs/messages.json | 4 +- apps/browser/src/_locales/cy/messages.json | 4 +- apps/browser/src/_locales/da/messages.json | 4 +- apps/browser/src/_locales/de/messages.json | 4 +- apps/browser/src/_locales/el/messages.json | 4 +- apps/browser/src/_locales/en_GB/messages.json | 4 +- apps/browser/src/_locales/en_IN/messages.json | 4 +- apps/browser/src/_locales/es/messages.json | 4 +- apps/browser/src/_locales/et/messages.json | 4 +- apps/browser/src/_locales/eu/messages.json | 4 +- apps/browser/src/_locales/fa/messages.json | 4 +- apps/browser/src/_locales/fi/messages.json | 6 +- apps/browser/src/_locales/fil/messages.json | 4 +- apps/browser/src/_locales/fr/messages.json | 4 +- apps/browser/src/_locales/gl/messages.json | 4 +- apps/browser/src/_locales/he/messages.json | 4 +- apps/browser/src/_locales/hi/messages.json | 4 +- apps/browser/src/_locales/hr/messages.json | 4 +- apps/browser/src/_locales/hu/messages.json | 4 +- apps/browser/src/_locales/id/messages.json | 4 +- apps/browser/src/_locales/it/messages.json | 4 +- apps/browser/src/_locales/ja/messages.json | 4 +- apps/browser/src/_locales/ka/messages.json | 4 +- apps/browser/src/_locales/km/messages.json | 4 +- apps/browser/src/_locales/kn/messages.json | 4 +- apps/browser/src/_locales/ko/messages.json | 4 +- apps/browser/src/_locales/lt/messages.json | 4 +- apps/browser/src/_locales/lv/messages.json | 4 +- apps/browser/src/_locales/ml/messages.json | 4 +- apps/browser/src/_locales/mr/messages.json | 4 +- apps/browser/src/_locales/my/messages.json | 4 +- apps/browser/src/_locales/nb/messages.json | 4 +- apps/browser/src/_locales/ne/messages.json | 4 +- apps/browser/src/_locales/nl/messages.json | 4 +- apps/browser/src/_locales/nn/messages.json | 4 +- apps/browser/src/_locales/or/messages.json | 4 +- apps/browser/src/_locales/pl/messages.json | 4 +- apps/browser/src/_locales/pt_BR/messages.json | 4 +- apps/browser/src/_locales/pt_PT/messages.json | 4 +- apps/browser/src/_locales/ro/messages.json | 20 ++--- apps/browser/src/_locales/ru/messages.json | 4 +- apps/browser/src/_locales/si/messages.json | 4 +- apps/browser/src/_locales/sk/messages.json | 4 +- apps/browser/src/_locales/sl/messages.json | 4 +- apps/browser/src/_locales/sr/messages.json | 82 +++++++++---------- apps/browser/src/_locales/sv/messages.json | 4 +- apps/browser/src/_locales/te/messages.json | 4 +- apps/browser/src/_locales/th/messages.json | 4 +- apps/browser/src/_locales/tr/messages.json | 4 +- apps/browser/src/_locales/uk/messages.json | 44 +++++----- apps/browser/src/_locales/vi/messages.json | 4 +- apps/browser/src/_locales/zh_CN/messages.json | 4 +- apps/browser/src/_locales/zh_TW/messages.json | 4 +- 60 files changed, 188 insertions(+), 188 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 2b6c041a06d..cf6bf851dd0 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 جيغابايت وحدة تخزين مشفرة لمرفقات الملفات." }, - "ppremiumSignUpTwoStep": { - "message": "خيارات تسجيل الدخول الإضافية من خطوتين مثل YubiKey و FIDO U2F و Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "نظافة كلمة المرور، صحة الحساب، وتقارير خرق البيانات للحفاظ على سلامة خزنتك." diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 347fc449fa4..283b03a17a9 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "Fayl qoşmaları üçün 1 GB şifrələnmiş saxlama sahəsi" }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F və Duo kimi iki mərhələli giriş seçimləri" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Anbarınızın təhlükəsiyini təmin etmək üçün parol gigiyenası, hesab sağlamlığı və verilənlərin pozulması hesabatları." diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 1df3d80b2e0..6aada06265d 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 ГБ зашыфраванага сховішча для далучаных файлаў." }, - "ppremiumSignUpTwoStep": { - "message": "Дадатковыя варыянты двухэтапнага ўваходу, такія як YubiKey, FIDO U2F і Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Гігіена пароляў, здароўе ўліковага запісу і справаздачы аб уцечках даных для забеспячэння бяспекі вашага сховішча." diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index ee3049ffe22..cf9ec1b8ed9 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB пространство за файлове, които се шифрират." }, - "ppremiumSignUpTwoStep": { - "message": "Двустепенно удостоверяване чрез YubiKey, FIDO U2F и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Проверки в списъците с публикувани пароли, проверка на регистрациите и доклади за пробивите в сигурността, което спомага трезорът ви да е допълнително защитен." diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index a7b49de5ab3..7d7151b58f6 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "ফাইল সংযুক্তির জন্য ১ জিবি এনক্রিপ্টেড স্থান।" }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F, ও Duo এর মতো অতিরিক্ত দ্বি-পদক্ষেপ লগইন বিকল্পগুলি।" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "আপনার ভল্টটি সুরক্ষিত রাখতে পাসওয়ার্ড স্বাস্থ্যকরন, অ্যাকাউন্ট স্বাস্থ্য এবং ডেটা লঙ্ঘনের প্রতিবেদন।" diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index cf2b0fd8a52..657ce243606 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index f00775b993e..fbe9e33c65d 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, - "ppremiumSignUpTwoStep": { - "message": "Opcions addicionals d'inici de sessió en dues passes com ara YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Opcions propietàries de doble factor com ara YubiKey i Duo." }, "ppremiumSignUpReports": { "message": "Requisits d'higiene de la contrasenya, salut del compte i informe d'infraccions de dades per mantenir la seguretat de la vostra caixa forta." diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 4d642f35640..a7bca0d78eb 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB šifrovaného úložiště pro přílohy." }, - "ppremiumSignUpTwoStep": { - "message": "Další možnosti dvoufázového přihlášení, jako je například YubiKey, FIDO U2F a Duo." + "premiumSignUpTwoStepOptions": { + "message": "Volby proprietálních dvoufázových přihlášení jako je YubiKey a Duo." }, "ppremiumSignUpReports": { "message": "Reporty o hygieně Vašich hesel, zdraví účtu a narušeních bezpečnosti." diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 6b278cbd93b..043a0fffead 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 7d258e9a5d1..d2ddc3381b5 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB krypteret lager til vedhæftede filer." }, - "ppremiumSignUpTwoStep": { - "message": "Yderligere to-trins login muligheder såsom YubiKey, FIDO U2F og Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietære totrins-login muligheder, såsom YubiKey og Duo." }, "ppremiumSignUpReports": { "message": "Adgangskodehygiejne, kontosundhed og rapporter om datalæk til at holde din boks sikker." diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 8dfe56577e8..500329ab9ca 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB verschlüsselter Speicherplatz für Dateianhänge." }, - "ppremiumSignUpTwoStep": { - "message": "Zusätzliche Zweifaktor-Anmeldung über YubiKey, FIDO U2F, und Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietäre Optionen für die Zwei-Faktor Authentifizierung wie YubiKey und Duo." }, "ppremiumSignUpReports": { "message": "Berichte über Kennworthygiene, Kontostatus und Datenschutzverletzungen, um deinen Tresor sicher zu halten." diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 5295936e303..e168fd93576 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB κρυπτογραφημένο αποθηκευτικό χώρο για συνημμένα αρχεία." }, - "ppremiumSignUpTwoStep": { - "message": "Πρόσθετες επιλογές σύνδεσης δύο βημάτων, όπως το YubiKey, το FIDO U2F και το Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Ασφάλεια κωδικών, υγεία λογαριασμού και αναφορές παραβίασης δεδομένων για να διατηρήσετε ασφαλές το vault σας." diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index b10b53657a3..1632b31ee03 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 8d46a4cbe26..b9f2a3784c3 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 71764078cf4..e43102b483e 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB de espacio cifrado en disco para adjuntos." }, - "ppremiumSignUpTwoStep": { - "message": "Métodos de autenticación en dos pasos adicionales como YubiKey, FIDO U2F y Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Higiene de contraseña, salud de la cuenta e informes de violaciones de datos para mantener su caja fuerte segura." diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 24c15541572..5fc83e82676 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB ulatuses krüpteeritud salvestusruum." }, - "ppremiumSignUpTwoStep": { - "message": "Lisavõimalused kaheastmeliseks kinnitamiseks, näiteks YubiKey, FIDO U2F ja Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Parooli hügieen, konto seisukord ja andmelekete raportid aitavad hoidlat turvalisena hoida." diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 4e5a6857c4c..ac54d0c4ea6 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "Eranskinentzako 1GB-eko zifratutako biltegia." }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F eta Duo bezalako bi urratseko saio hasierarako aukera gehigarriak." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Pasahitzaren higienea, kontuaren egoera eta datu-bortxaketen txostenak, kutxa gotorra seguru mantentzeko." diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index b7aadc46e30..e1057038933 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "۱ گیگابایت فضای ذخیره سازی رمزگذاری شده برای پیوست های پرونده." }, - "ppremiumSignUpTwoStep": { - "message": "گزینه‌های ورود دو مرحله‌ای اضافی مانند YubiKey, FIDO U2F و Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "گزارش‌های بهداشت رمز عبور، سلامت حساب و نقض داده‌ها برای ایمن نگهداشتن گاوصندوق شما." diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 4ab409bfcb2..69492afd7f4 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, - "ppremiumSignUpTwoStep": { - "message": "Muita kaksivaiheisen kirjautumisen todennusmenetelmiä kuten YubiKey, FIDO U2F ja Duo Security." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Salasanahygienian, tilin terveyden ja tietovuotojen raportointitoiminnot pitävät holvisi turvassa." @@ -2310,7 +2310,7 @@ "message": "pakollinen" }, "search": { - "message": "Etsi" + "message": "Hae" }, "inputMinLength": { "message": "Syötteen tulee sisältää ainakin $COUNT$ merkkiä.", diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 802d686c5af..be101c80b41 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage para sa mga file attachment." }, - "ppremiumSignUpTwoStep": { - "message": "Dagdag na dalawang hakbang na login option gaya ng YubiKey, FIDO U2F, at Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Pasahod higiyena, kalusugan ng account, at mga ulat sa data breach upang panatilihing ligtas ang iyong vault." diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 67c126dd9fc..b554c619a5f 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 Go de stockage chiffré pour les fichiers joints." }, - "ppremiumSignUpTwoStep": { - "message": "Options additionnelles d'identification à deux étapes telles que YubiKey, FIDO U2F et Duo." + "premiumSignUpTwoStepOptions": { + "message": "Options de connexion propriétaires à deux facteurs telles que YubiKey et Duo." }, "ppremiumSignUpReports": { "message": "Hygiène du mot de passe, santé du compte et rapports sur les brèches de données pour assurer la sécurité de votre coffre." diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index ebdc30e269d..752935ce81a 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 ג'יגה של מקום אחסון עבור קבצים מצורפים." }, - "ppremiumSignUpTwoStep": { - "message": "אפשרויות כניסה דו שלבית מתקדמות כמו YubiKey, FIDO U2F, וגם Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "היגיינת סיסמאות, מצב בריאות החשבון, ודיווחים מעודכנים על פרצות חדשות בכדי לשמור על הכספת שלך בטוחה." diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 7d08de998db..d5c465e68bf 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB of encrypted file storage." }, - "ppremiumSignUpTwoStep": { - "message": "अतिरिक्त दो-चरण लॉगिन विकल्प जैसे YubiKey, FIDO U2F, और डुओ।" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "अपनी वॉल्ट को सुरक्षित रखने के लिए पासवर्ड स्वच्छता, खाता स्वास्थ्य और डेटा उल्लंघन रिपोर्ट।" diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 18f0d151312..db0fefbbffb 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB šifriranog prostora za pohranu podataka." }, - "ppremiumSignUpTwoStep": { - "message": "Dodatne mogućnosti za prijavu dvostrukom autentifikacijom kao što su YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Higijenu lozinki, zdravlje računa i izvještaje o krađi podatak radi zaštite svojeg trezora." diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index e32509dbbc3..8bc89651d53 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB titkosított tárhely a fájlmellékleteknek." }, - "ppremiumSignUpTwoStep": { - "message": "További két lépcsős bejelentkezés lehetőségek, mint például YubiKey, FIDO U2F és Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Jelszó higiénia, fiók biztonság és adatszivárgási jelentések a széf biztonsága érdekében." diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index b53f92ba44c..d8f6698f4ce 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB penyimpanan berkas yang dienkripsi." }, - "ppremiumSignUpTwoStep": { - "message": "Pilihan info masuk dua langkah tambahan seperti YubiKey, FIDO U2F, dan Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Kebersihan kata sandi, kesehatan akun, dan laporan kebocoran data untuk tetap menjaga keamanan brankas Anda." diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 75154c2453f..e17ea019409 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB di spazio di archiviazione criptato per gli allegati." }, - "ppremiumSignUpTwoStep": { - "message": "Più opzioni di verifica in due passaggi come YubiKey, FIDO U2F, e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Sicurezza delle password, integrità dell'account, e rapporti su violazioni di dati per mantenere sicura la tua cassaforte." diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 93af04dd99f..e979237988c 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1GB の暗号化されたファイルストレージ" }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey、FIDO U2F、Duoなどの追加の2段階認証ログインオプション" + "premiumSignUpTwoStepOptions": { + "message": "YubiKey、Duo などのプロプライエタリな2段階認証オプション。" }, "ppremiumSignUpReports": { "message": "保管庫を安全に保つための、パスワードやアカウントの健全性、データ侵害に関するレポート" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index c8c379ec377..a619c47eaf4 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index daaf3011f6f..fd01869139d 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "ಫೈಲ್ ಲಗತ್ತುಗಳಿಗಾಗಿ 1 ಜಿಬಿ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿದ ಸಂಗ್ರಹ." }, - "ppremiumSignUpTwoStep": { - "message": "ಹೆಚ್ಚುವರಿ ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಆಯ್ಕೆಗಳಾದ ಯೂಬಿಕೆ, ಎಫ್‌ಐಡಿಒ ಯು 2 ಎಫ್, ಮತ್ತು ಡ್ಯುವೋ." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "ನಿಮ್ಮ ವಾಲ್ಟ್ ಅನ್ನು ಸುರಕ್ಷಿತವಾಗಿರಿಸಲು ಪಾಸ್ವರ್ಡ್ ನೈರ್ಮಲ್ಯ, ಖಾತೆ ಆರೋಗ್ಯ ಮತ್ತು ಡೇಟಾ ಉಲ್ಲಂಘನೆ ವರದಿಗಳು." diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 4d302bc5834..f982332a503 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1GB의 암호화된 파일 저장소." }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey나 FIDO U2F, Duo 등의 추가적인 2단계 인증 옵션." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "보관함을 안전하게 유지하기 위한 암호 위생, 계정 상태, 데이터 유출 보고서" diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 72748ba4a37..a3ba40c1fd9 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB užšifruotos vietos diske bylų prisegimams." }, - "ppremiumSignUpTwoStep": { - "message": "Papildomos dviejų žingsių prisijungimo opcijos, tokios kaip YubiKey, FIDO U2F ir Duo." + "premiumSignUpTwoStepOptions": { + "message": "Patentuotos dviejų žingsnių prisijungimo parinktys, tokios kaip YubiKey ir Duo." }, "ppremiumSignUpReports": { "message": "Slaptažodžio higiena, prieigos sveikata ir duomenų nutekinimo ataskaitos, kad tavo saugyklas būtų saugus." diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index ee46e51a6b2..cbf2c469124 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB šifrētas krātuves datņu pielikumiem." }, - "ppremiumSignUpTwoStep": { - "message": "Tādas papildu divpakāpju pieteikšanās iespējas kā YubiKey, FIDO U2F un Duo." + "premiumSignUpTwoStepOptions": { + "message": "Tādas slēgtā pirmavota divpakāpju pieteikšanās iespējas kā YubiKey un Duo." }, "ppremiumSignUpReports": { "message": "Paroļu higiēnas, konta veselības un datu noplūžu pārskati, lai uzturētu glabātavu drošu." diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index c7b3bd91b0d..416b35f78ec 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "ഫയൽ അറ്റാച്ചുമെന്റുകൾക്കായി 1 ജിബി എൻക്രിപ്റ്റുചെയ്‌ത സംഭരണം." }, - "ppremiumSignUpTwoStep": { - "message": "രണ്ട്-ഘട്ട പ്രവേശന ഓപ്ഷനുകളായ Yubikey, FIDO U2F, Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "നിങ്ങളുടെ വാൾട് സൂക്ഷിക്കുന്നതിന്. പാസ്‌വേഡ് ശുചിത്വം, അക്കൗണ്ട് ആരോഗ്യം, ഡാറ്റ ലംഘന റിപ്പോർട്ടുകൾ." diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 5943fb9724e..e12aa7f8a65 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 8b3889b799d..e806ea0a781 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB med kryptert fillagring for filvedlegg." }, - "ppremiumSignUpTwoStep": { - "message": "Ytterligere 2-trinnsinnloggingsmuligheter, slik som YubiKey, FIDO U2F, og Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Passordhygiene, kontohelse, og databruddsrapporter som holder hvelvet ditt trygt." diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index ed6f5394af3..f34016f70f0 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB versleutelde opslag voor bijlagen." }, - "ppremiumSignUpTwoStep": { - "message": "Extra opties voor tweestapsaanmelding zoals YubiKey, FIDO U2F en Duo." + "premiumSignUpTwoStepOptions": { + "message": "Eigen opties voor tweestapsaanmelding zoals YubiKey en Duo." }, "ppremiumSignUpReports": { "message": "Wachtwoordhygiëne, gezondheid van je account en datalekken om je kluis veilig te houden." diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 37e5b701472..75f416b14e4 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB miejsca na zaszyfrowane załączniki." }, - "ppremiumSignUpTwoStep": { - "message": "Dodatkowe opcje logowania dwustopniowego, takie jak klucze YubiKey, FIDO U2F oraz Duo." + "premiumSignUpTwoStepOptions": { + "message": "Własnościowe opcje logowania dwuetapowego, takie jak YubiKey i Duo." }, "ppremiumSignUpReports": { "message": "Raporty bezpieczeństwa haseł, stanu konta i raporty wycieków danych, aby Twoje dane były bezpieczne." diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 0dd3ed1eee6..f8db10e49f8 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB de armazenamento de arquivos encriptados." }, - "ppremiumSignUpTwoStep": { - "message": "Opções de autenticação de duas etapas adicionais como YubiKey, FIDO U2F, e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Higiene de senha, saúde da conta, e relatórios sobre violação de dados para manter o seu cofre seguro." diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 68a69fcf9c5..074ddb150e0 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, - "ppremiumSignUpTwoStep": { - "message": "Opções adicionais de verificação de dois passos, como YubiKey, FIDO U2F e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Higiene de palavras-passe, saúde da conta e relatórios de violação de dados para manter o seu cofre seguro." diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 6f577a8da57..20c44ddcca2 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -196,13 +196,13 @@ "message": "Ajutor și feedback" }, "helpCenter": { - "message": "Bitwarden Help center" + "message": "Centrul de Ajutor Bitwarden" }, "communityForums": { - "message": "Explore Bitwarden community forums" + "message": "Explorează forumurile comunității Bitwarden" }, "contactSupport": { - "message": "Contact Bitwarden support" + "message": "Contactați asistența Bitwarden" }, "sync": { "message": "Sincronizare" @@ -442,7 +442,7 @@ "message": "Este necesară rescrierea parolei principale." }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "Parola principală trebuie să aibă cel puțin $VALUE$ caractere.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -634,10 +634,10 @@ "message": "Actualizare" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "Deblochează seiful Bitwarden pentru a finaliza solicitarea de completare automată." }, "notificationUnlock": { - "message": "Unlock" + "message": "Deblocare" }, "enableContextMenuItem": { "message": "Afișați opțiunile meniului contextual" @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB spațiu de stocare criptat pentru atașamente de fișiere." }, - "ppremiumSignUpTwoStep": { - "message": "Opțiuni adiționale de conectare în două etape, cum ar fi YubiKey, FIDO U2F și Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Rapoarte privind igiena parolelor, sănătatea contului și breșele de date pentru a vă păstra seiful în siguranță." @@ -985,7 +985,7 @@ "message": "Dacă se detectează un formular de autentificare, completați-l automat la încărcarea paginii web." }, "experimentalFeature": { - "message": "Compromised or untrusted websites can exploit auto-fill on page load." + "message": "Site-urile web compromise sau nesigure pot exploata funcția de autocompletare la încărcarea paginii." }, "learnMoreAboutAutofill": { "message": "Learn more about auto-fill" @@ -1468,7 +1468,7 @@ "message": "Articolul s-a completat automat " }, "insecurePageWarning": { - "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + "message": "Avertisment: Aceasta este o pagină HTTP nesecurizată și orice informație pe care o trimiteți poate fi văzută și modificată de alte persoane. Această Parolă a fost salvată inițial pe o pagină securizată (HTTPS)." }, "insecurePageWarningFillPrompt": { "message": "Do you still wish to fill this login?" diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index d9614a63c2c..738193fb48a 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 ГБ зашифрованного хранилища для вложенных файлов." }, - "ppremiumSignUpTwoStep": { - "message": "Дополнительные варианты двухэтапной аутентификации, такие как YubiKey, FIDO U2F и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Проприетарные варианты двухэтапной аутентификации, такие как YubiKey или Duo." }, "ppremiumSignUpReports": { "message": "Гигиена паролей, здоровье аккаунта и отчеты об утечках данных для обеспечения безопасности вашего хранилища." diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 0d9a9648b7e..cf3ac370c6d 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "ගොනු ඇමුණුම් සඳහා 1 GB සංකේතාත්මක ගබඩා." }, - "ppremiumSignUpTwoStep": { - "message": "එවැනි YuBiKey, FIDO U2F, සහ Duo ලෙස අතිරේක පියවර දෙකක් පිවිසුම් විකල්ප." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "ඔබගේ සුරක්ෂිතාගාරය ආරක්ෂිතව තබා ගැනීම සඳහා මුරපදය සනීපාරක්ෂාව, ගිණුම් සෞඛ්යය සහ දත්ත උල්ලං ach නය වාර්තා කරයි." diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index c06ad279ec6..1b90bc94053 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB šifrovaného úložiska." }, - "ppremiumSignUpTwoStep": { - "message": "Ďalšie možnosti dvojstupňového prihlásenia ako YubiKey, FIDO U2F a Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Správy o sile hesla, zabezpečení účtov a únikoch dát ktoré vám pomôžu udržať vaše kontá v bezpečí." diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index dba7c971bd7..cae2464dcb1 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB šifriranega prostora za shrambo podatkov." }, - "ppremiumSignUpTwoStep": { - "message": "Dodatne možnosti za prijavo v dveh korakih, n.pr. YubiKey, FIDO U2F in Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Higiena gesel, zdravje računa in poročila o kraji podatkov, ki vam pomagajo ohraniti varnost vašega trezorja." diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 155c5f6acc5..d4e5ee4f2a4 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -339,7 +339,7 @@ "message": "Остало" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Подесите метод откључавања да бисте променили радњу временског ограничења сефа." }, "rateExtension": { "message": "Оцени овај додатак" @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1ГБ шифровано складиште за прилоге." }, - "ppremiumSignUpTwoStep": { - "message": "Додатне опције пријаве у два корака као што су YubiKey, FIDO U2F, и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Извештаји о хигијени лозинки, здравственом стању налога и кршењу података да бисте заштитили сеф." @@ -1606,10 +1606,10 @@ "message": "Биометрија прегледача није подржана на овом уређају." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "Биометрија није успела" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "Биометрија се не може завршити, размислите о коришћењу главне лозинке или одјавите се. Ако се ово настави, контактирајте подршку Bitwarden-а." }, "nativeMessaginPermissionErrorTitle": { "message": "Дозвола није дата" @@ -2153,7 +2153,7 @@ "message": "Обавештење је послато на ваш уређај." }, "loginInitiated": { - "message": "Login initiated" + "message": "Пријава је покренута" }, "exposedMasterPassword": { "message": "Изложена главна лозинка" @@ -2240,25 +2240,25 @@ "message": "Отвара се у новом прозору" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Потребно је одобрење уређаја. Изаберите опцију одобрења испод:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Запамти овај уређај" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Искључите ако се користи јавни уређај" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Одобри са мојим другим уређајем" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Затражити одобрење администратора" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Одобрити са главном лозинком" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Потребан је SSO идентификатор организације." }, "eu": { "message": "EU", @@ -2280,40 +2280,40 @@ "message": "Приказ" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Налог је успешно креиран!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Захтевано је одобрење администратора" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Ваш захтев је послат вашем администратору." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Бићете обавештени када буде одобрено." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Имате проблема са пријављивањем?" }, "loginApproved": { - "message": "Login approved" + "message": "Пријава је одобрена" }, "userEmailMissing": { - "message": "User email missing" + "message": "Недостаје имејл корисника" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Уређај поуздан" }, "inputRequired": { - "message": "Input is required." + "message": "Унос је потребан." }, "required": { - "message": "required" + "message": "обавезно" }, "search": { - "message": "Search" + "message": "Тражи" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Унос трба имати најмање $COUNT$ слова.", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2322,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Унос не сме бити већи од $COUNT$ карактера.", "placeholders": { "count": { "content": "$1", @@ -2331,7 +2331,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Следећи знакови нису дозвољени: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2340,7 +2340,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Вредност мора бити најмање $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2349,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Вредност не сме бити већа од $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2358,17 +2358,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 или више имејлова су неважећи" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Унос не сме да садржи само размак.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Унос није имејл." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ поље(а) изнад захтевај(у) вашу пажњу.", "placeholders": { "count": { "content": "$1", @@ -2377,22 +2377,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Одабрати --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Тип за филтрирање --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Преузимање опција..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Нема предмета" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Обриши све" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ још $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2401,10 +2401,10 @@ } }, "submenu": { - "message": "Submenu" + "message": "Под-мени" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Промени проширење", "description": "Toggling an expand/collapse state." } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index f3275fce001..7ab058680c4 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB lagring av krypterade filer." }, - "ppremiumSignUpTwoStep": { - "message": "Ytterligare alternativ för tvåstegsverifiering såsom YubiKey, FIDO U2F och Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Lösenordshygien, kontohälsa och dataintrångsrapporter för att hålla ditt valv säkert." diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 56640a8af8e..6aea5876eac 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 1cc5e9bc50c..7fcf7835119 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB of encrypted file storage." }, - "ppremiumSignUpTwoStep": { - "message": "ตัวเลือกการเข้าสู่ระบบแบบสองขั้นตอนเพิ่มเติม เช่น YubiKey, FIDO U2F และ Duo" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "สุขอนามัยของรหัสผ่าน ความสมบูรณ์ของบัญชี และรายงานการละเมิดข้อมูลเพื่อให้ตู้นิรภัยของคุณปลอดภัย" diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 8bcdf1f6580..4134a27d265 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "Dosya ekleri için 1 GB şifrelenmiş depolama." }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F ve Duo gibi iki aşamalı giriş seçenekleri." + "premiumSignUpTwoStepOptions": { + "message": "YubiKey ve Duo gibi marka bazlı iki aşamalı giriş seçenekleri." }, "ppremiumSignUpReports": { "message": "Kasanızı güvende tutmak için parola hijyeni, hesap sağlığı ve veri ihlali raporları." diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index dfa5ac4c7d3..2d1a0b66291 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 ГБ зашифрованого сховища для файлів." }, - "ppremiumSignUpTwoStep": { - "message": "Додаткові можливості двоетапної перевірки, наприклад, YubiKey, FIDO U2F та Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Гігієна паролів, здоров'я облікового запису, а також звіти про вразливості даних, щоб зберігати ваше сховище в безпеці." @@ -2304,16 +2304,16 @@ "message": "Довірений пристрій" }, "inputRequired": { - "message": "Input is required." + "message": "Необхідно ввести дані." }, "required": { - "message": "required" + "message": "обов'язково" }, "search": { - "message": "Search" + "message": "Пошук" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Введені дані мають бути довжиною принаймні $COUNT$ символів.", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2322,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Вхідне значення не повинно перевищувати $COUNT$ символів.", "placeholders": { "count": { "content": "$1", @@ -2331,7 +2331,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Вказані символи заборонені: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2340,7 +2340,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Значення має бути принаймні $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2349,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Значення не може перевищувати $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2358,17 +2358,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 або більше адрес е-пошти недійсні" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Введене значення не повинно містити лише пробіл.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Введені дані не є адресою е-пошти." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ поле (поля) вище потребують вашої уваги.", "placeholders": { "count": { "content": "$1", @@ -2377,22 +2377,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Оберіть--" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Введіть для фільтрування --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Параметри отримання..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Нічого не знайдено" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Очистити все" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ ще $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2401,10 +2401,10 @@ } }, "submenu": { - "message": "Submenu" + "message": "Підменю" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Згорнути/розгорнути", "description": "Toggling an expand/collapse state." } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 7faabed46e0..5d71340db52 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1GB bộ nhớ lưu trữ tập tin được mã hóa." }, - "ppremiumSignUpTwoStep": { - "message": "Tuỳ chọn đăng nhập 2 bước bổ sung như YubiKey, FIDO U2F, và Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "Thanh lọc mật khẩu, kiểm tra an toàn tài khoản và các báo cáo rò rĩ dữ liệu là để giữ cho kho của bạn an toàn." diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index eb02a54f453..1fc419d0d16 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "1 GB 文件附件加密存储。" }, - "ppremiumSignUpTwoStep": { - "message": "额外的两步登录选项,如 YubiKey、FIDO U2F 和 Duo。" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "密码健康、账户体检以及数据泄露报告,保障您的密码库安全。" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index a0a2c9e3eb3..68eb917021e 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -795,8 +795,8 @@ "ppremiumSignUpStorage": { "message": "用於檔案附件的 1 GB 加密儲存空間。" }, - "ppremiumSignUpTwoStep": { - "message": "YubiKey、FIDO U2F 和 Duo 等額外的兩步驟登入選項。" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "ppremiumSignUpReports": { "message": "密碼健康度檢查、提供帳戶體檢以及資料外洩報告,以保障您的密碼庫安全。" From 4e2f742aea8b9150518d045541ae46eb4dd1dafd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:45:39 +0000 Subject: [PATCH 083/135] Autosync the updated translations (#6165) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 4 +- apps/desktop/src/locales/ar/messages.json | 4 +- apps/desktop/src/locales/az/messages.json | 4 +- apps/desktop/src/locales/be/messages.json | 4 +- apps/desktop/src/locales/bg/messages.json | 4 +- apps/desktop/src/locales/bn/messages.json | 4 +- apps/desktop/src/locales/bs/messages.json | 4 +- apps/desktop/src/locales/ca/messages.json | 4 +- apps/desktop/src/locales/cs/messages.json | 4 +- apps/desktop/src/locales/cy/messages.json | 4 +- apps/desktop/src/locales/da/messages.json | 4 +- apps/desktop/src/locales/de/messages.json | 4 +- apps/desktop/src/locales/el/messages.json | 4 +- apps/desktop/src/locales/en_GB/messages.json | 4 +- apps/desktop/src/locales/en_IN/messages.json | 4 +- apps/desktop/src/locales/eo/messages.json | 4 +- apps/desktop/src/locales/es/messages.json | 4 +- apps/desktop/src/locales/et/messages.json | 4 +- apps/desktop/src/locales/eu/messages.json | 4 +- apps/desktop/src/locales/fa/messages.json | 4 +- apps/desktop/src/locales/fi/messages.json | 4 +- apps/desktop/src/locales/fil/messages.json | 4 +- apps/desktop/src/locales/fr/messages.json | 4 +- apps/desktop/src/locales/gl/messages.json | 4 +- apps/desktop/src/locales/he/messages.json | 4 +- apps/desktop/src/locales/hi/messages.json | 4 +- apps/desktop/src/locales/hr/messages.json | 4 +- apps/desktop/src/locales/hu/messages.json | 4 +- apps/desktop/src/locales/id/messages.json | 4 +- apps/desktop/src/locales/it/messages.json | 4 +- apps/desktop/src/locales/ja/messages.json | 8 +- apps/desktop/src/locales/ka/messages.json | 4 +- apps/desktop/src/locales/km/messages.json | 4 +- apps/desktop/src/locales/kn/messages.json | 4 +- apps/desktop/src/locales/ko/messages.json | 4 +- apps/desktop/src/locales/lt/messages.json | 2419 ++++++++++++++++++ apps/desktop/src/locales/lv/messages.json | 4 +- apps/desktop/src/locales/me/messages.json | 4 +- apps/desktop/src/locales/ml/messages.json | 4 +- apps/desktop/src/locales/mr/messages.json | 4 +- apps/desktop/src/locales/my/messages.json | 4 +- apps/desktop/src/locales/nb/messages.json | 4 +- apps/desktop/src/locales/ne/messages.json | 4 +- apps/desktop/src/locales/nl/messages.json | 4 +- apps/desktop/src/locales/nn/messages.json | 4 +- apps/desktop/src/locales/or/messages.json | 4 +- apps/desktop/src/locales/pl/messages.json | 4 +- apps/desktop/src/locales/pt_BR/messages.json | 4 +- apps/desktop/src/locales/pt_PT/messages.json | 4 +- apps/desktop/src/locales/ro/messages.json | 4 +- apps/desktop/src/locales/ru/messages.json | 4 +- apps/desktop/src/locales/si/messages.json | 4 +- apps/desktop/src/locales/sk/messages.json | 4 +- apps/desktop/src/locales/sl/messages.json | 4 +- apps/desktop/src/locales/sr/messages.json | 78 +- apps/desktop/src/locales/sv/messages.json | 4 +- apps/desktop/src/locales/te/messages.json | 4 +- apps/desktop/src/locales/th/messages.json | 4 +- apps/desktop/src/locales/tr/messages.json | 4 +- apps/desktop/src/locales/uk/messages.json | 42 +- apps/desktop/src/locales/vi/messages.json | 4 +- apps/desktop/src/locales/zh_CN/messages.json | 4 +- apps/desktop/src/locales/zh_TW/messages.json | 28 +- 63 files changed, 2613 insertions(+), 194 deletions(-) create mode 100644 apps/desktop/src/locales/lt/messages.json diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 7e1ee03ea58..fa880624311 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GG geënkripteerde berging vir lêeraanhegsels." }, - "premiumSignUpTwoStep": { - "message": "Bykomende tweestapaantekenopsies soos YubiKey, FIDO U2F en Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Wagwoordhigiëne, rekeningwelstand en databreukverslae om u kluis veilig te hou." diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index a10a5d6ee7b..914d2026267 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 جيغابايت وحدة تخزين مشفرة لمرفقات الملفات." }, - "premiumSignUpTwoStep": { - "message": "خيارات تسجيل الدخول الإضافية من خطوتين مثل YubiKey و FIDO U2F و Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "نظافة كلمة المرور، صحة الحساب، وتقارير خرق البيانات للحفاظ على سلامة خزنتك." diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 44fd72128ca..3256e1198b2 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "Fayl qoşmaları üçün 1 GB şifrələnmiş saxlama sahəsi." }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F və Duo kimi iki mərhələli giriş seçimləri." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Anbarınızın təhlükəsiyini təmin etmək üçün parol gigiyenası, hesab sağlamlığı və verilənlərin pozulması hesabatları." diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 680a7599638..fc45db18b3f 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 ГБ зашыфраванага сховішча для далучаных файлаў." }, - "premiumSignUpTwoStep": { - "message": "Дадатковыя варыянты двухэтапнага ўваходу, такія як YubiKey, FIDO U2F і Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Гігіена пароляў, здароўе ўліковага запісу і справаздачы аб уцечках даных для забеспячэння бяспекі вашага сховішча." diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index ab6f93166ca..2283ccbde6e 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 ГБ пространство за файлове, които се шифроват." }, - "premiumSignUpTwoStep": { - "message": "Двустепенно удостоверяване чрез YubiKey, FIDO U2F и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Проверки в списъците с публикувани пароли, проверка на регистрациите и доклади за пробивите в сигурността, което спомага трезорът ви да е допълнително защитен." diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 5c155e80210..e59dd9de8b9 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "ফাইল সংযুক্তির জন্য ১ জিবি এনক্রিপ্টেড স্থান।" }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F, ও Duo এর মতো অতিরিক্ত দ্বি-পদক্ষেপ লগইন বিকল্পগুলি।" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "আপনার ভল্টটি সুরক্ষিত রাখতে পাসওয়ার্ড স্বাস্থ্যকরন, অ্যাকাউন্ট স্বাস্থ্য এবং ডেটা লঙ্ঘনের প্রতিবেদন।" diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 3571229b42c..c88f7f392f1 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB šifriranog prostora za pohranu podataka." }, - "premiumSignUpTwoStep": { - "message": "Dodatne mogućnosti za prijavu u dva koraka kao što su YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Higijenu lozinki, zdravlje računa i izvještaje o krađi podataka radi zaštite svojeg trezora." diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 0e11e49b8f4..9850e4b82a9 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, - "premiumSignUpTwoStep": { - "message": "Opcions addicionals d'inici de sessió en dues passes com ara YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Requisits d'higiene de la contrasenya, salut del compte i informe d'infraccions de dades per mantenir la seguretat de la vostra caixa forta." diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index a931ae1b14b..4ce336733f6 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrovaného uložiště pro přílohy." }, - "premiumSignUpTwoStep": { - "message": "Další možnosti dvoufázového přihlášení, jako je například YubiKey, FIDO U2F a Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Reporty o hygieně Vašich hesel, zdraví účtu a narušeních bezpečnosti." diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index e398565d90c..157e1313ff5 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB krypteret lagerplads til filvedhæftninger." }, - "premiumSignUpTwoStep": { - "message": "Yderligere totrins-loginmuligheder, såsom YubiKey, FIDO U2F og Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietære totrins-login muligheder, såsom YubiKey og Duo." }, "premiumSignUpReports": { "message": "Adgangskodehygiejne, kontosundhed og rapporter om datalæk til at holde din boks sikker." diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index b238af9b9d7..d16eb636284 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB verschlüsselter Speicherplatz für Dateianhänge." }, - "premiumSignUpTwoStep": { - "message": "Zusätzliche Zwei-Faktor-Anmeldung über YubiKey, FIDO U2F, und Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietäre Optionen für die Zwei-Faktor Authentifizierung wie YubiKey und Duo." }, "premiumSignUpReports": { "message": "Berichte über Kennworthygiene, Kontostatus und Datenschutzverletzungen, um Ihren Tresor sicher zu halten." diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 10f77e91baa..fd800e8d713 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB κρυπτογραφημένο αποθηκευτικό χώρο για συνημμένα αρχεία." }, - "premiumSignUpTwoStep": { - "message": "Πρόσθετες επιλογές σύνδεσης δύο βημάτων, όπως το YubiKey, το FIDO U2F και το Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Ασφάλεια κωδικών, υγιής λογαριασμός και αναφορές παραβίασης δεδομένων για να διατηρήσετε ασφαλή τη λίστα σας." diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 7af01bc7cc4..6bc9772eb88 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 811d706fbdd..363648da567 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 1d93cc95b66..66195d90739 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index e662e3455c5..b40c409d1b0 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1GB de espacio en disco cifrado." }, - "premiumSignUpTwoStep": { - "message": "Métodos de autenticación en dos pasos adicionales como YubiKey, FIDO U2F y Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Higiene de contraseña, salud de la cuenta e informes de violaciones de datos para mantener tu caja fuerte segura." diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 64e3bc19e06..fb74084186c 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB ulatuses krüpteeritud salvestusruum." }, - "premiumSignUpTwoStep": { - "message": "Lisavõimalused kaheastmeliseks kinnitamiseks, näiteks YubiKey, FIDO U2F ja Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Parooli hügieen, konto seisukord ja andmelekete raportid aitavad hoidlat turvalisena hoida." diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index c590b004d6f..4c3931490df 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "Eranskinentzako 1GB-eko zifratutako biltegia." }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F eta Duo bezalako bi urratseko saio hasierarako aukera gehigarriak." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Pasahitzaren higienea, kontuaren egoera eta datu-bortxaketen txostenak, kutxa gotorra seguru mantentzeko." diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 5661678b37a..9e5f6f8468c 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "۱ گیگابایت فضای ذخیره‌سازی رمزنگاری شده برای پرونده‌های پیوست." }, - "premiumSignUpTwoStep": { - "message": "گزینه‌های ورود دو مرحله‌ای اضافی مانند YubiKey, FIDO U2F و Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "گزارش‌های بهداشت رمز عبور، سلامت حساب و نقض داده‌ها برای ایمن نگهداشتن گاوصندوق شما." diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index c3e4de27aab..9025306a708 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, - "premiumSignUpTwoStep": { - "message": "Muita kaksivaiheisen kirjautumisen todennusmenetelmiä kuten YubiKey, FIDO U2F ja Duo Security." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Salasanahygienian, tilin terveyden ja tietovuotojen raportointitoiminnot pitävät holvisi turvassa." diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 996d1c28277..87b9bde0c46 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB naka encrypt na imbakan para sa mga attachment ng file." }, - "premiumSignUpTwoStep": { - "message": "Karagdagang dalawang hakbang na mga pagpipilian sa pag login tulad ng YubiKey, FIDO U2F, at Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Kalinisan ng password, kalusugan ng account, at mga ulat ng paglabag sa data upang mapanatiling ligtas ang iyong vault." diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 6049b364fd0..3f249000bd5 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 Go de stockage chiffré pour les fichiers joints." }, - "premiumSignUpTwoStep": { - "message": "Options additionnelles d'identification à deux étapes telles que YubiKey, FIDO U2F et Duo." + "premiumSignUpTwoStepOptions": { + "message": "Options de connexion propriétaires à deux facteurs telles que YubiKey et Duo." }, "premiumSignUpReports": { "message": "Hygiène du mot de passe, santé du compte et rapports sur les brèches de données pour assurer la sécurité de votre coffre." diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index b06507da0c2..ad625991fa1 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 ג'יגה של מקום אחסון מוצפן עבור קבצים מצורפים." }, - "premiumSignUpTwoStep": { - "message": "אפשרויות כניסה דו שלבית מתקדמות כמו YubiKey, FIDO U2F, וDuo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "היגיינת סיסמאות, מצב בריאות החשבון, ודיווחים מעודכנים על פרצות חדשות בכדי לשמור על הכספת שלך בטוחה." diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 024bcfb2dfe..156a431a2d2 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 6f1e9e08272..42966a51575 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB šifriranog prostora za pohranu podataka." }, - "premiumSignUpTwoStep": { - "message": "Dodatne mogućnosti za prijavu dvostrukom autentifikacijom kao što su YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Higijenu lozinki, zdravlje računa i izvještaje o krađi podatak radi zaštite svojeg trezora." diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 283d30e0ee0..796da6fdece 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB titkosított fájlmelléklet tárhely." }, - "premiumSignUpTwoStep": { - "message": "További olyan kétlépcsős bejelentkezési opciók mint a YubiKey, FIDO U2F és Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Jelszó higiénia, felhasználói fiók biztonsága, és adatszivárgási jelentések a széf biztonsága érdekében." diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 491ede25dc9..b976d1b8fd9 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB penyimpanan berkas yang dienkripsi." }, - "premiumSignUpTwoStep": { - "message": "Pilihan info masuk dua langkah tambahan seperti YubiKey, FIDO U2F, dan Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Kebersihan kata sandi, kesehatan akun, dan laporan pelanggaran data untuk menjaga brankas Anda tetap aman." diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index fc13e4220f7..f294888cc38 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB di spazio di archiviazione criptato per gli allegati." }, - "premiumSignUpTwoStep": { - "message": "Più opzioni di verifica in due passaggi come YubiKey, FIDO U2F, e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Sicurezza delle password, integrità dell'account e rapporti sulle violazioni dei dati per mantenere la tua cassaforte sicura." diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 393f203fb84..f8c11aca7e5 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1GB の暗号化されたファイルストレージ。" }, - "premiumSignUpTwoStep": { - "message": "YubiKey、FIDO U2F、Duoなどの追加の2段階認証ログインオプション" + "premiumSignUpTwoStepOptions": { + "message": "YubiKey、Duo などのプロプライエタリな2段階認証オプション。" }, "premiumSignUpReports": { "message": "保管庫を安全に保つための、パスワードやアカウントの健全性、データ侵害に関するレポート。" @@ -1681,7 +1681,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "myVault": { - "message": "保管庫" + "message": "自分の保管庫" }, "text": { "message": "テキスト" @@ -2049,7 +2049,7 @@ "message": "組織の検索" }, "searchMyVault": { - "message": "保管庫を検索" + "message": "自分の保管庫内を検索" }, "forwardedEmail": { "message": "転送されたメールエイリアス" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 4be76af8b3e..bfc29e570a1 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "ಫೈಲ್ ಲಗತ್ತುಗಳಿಗಾಗಿ 1 ಜಿಬಿ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿದ ಸಂಗ್ರಹ." }, - "premiumSignUpTwoStep": { - "message": "ಹೆಚ್ಚುವರಿ ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಆಯ್ಕೆಗಳಾದ ಯೂಬಿಕೆ, ಎಫ್‌ಐಡಿಒ ಯು 2 ಎಫ್, ಮತ್ತು ಡ್ಯುವೋ." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "ನಿಮ್ಮ ವಾಲ್ಟ್ ಅನ್ನು ಸುರಕ್ಷಿತವಾಗಿರಿಸಲು ಪಾಸ್ವರ್ಡ್ ನೈರ್ಮಲ್ಯ, ಖಾತೆ ಆರೋಗ್ಯ ಮತ್ತು ಡೇಟಾ ಉಲ್ಲಂಘನೆ ವರದಿಗಳು." diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 8948b61ee85..7bff705c8ec 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1GB의 암호화된 파일 저장소." }, - "premiumSignUpTwoStep": { - "message": "YubiKey나 FIDO U2F, Duo 등의 추가적인 2단계 인증 옵션." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "보관함을 안전하게 유지하기 위한 암호 위생, 계정 상태, 데이터 유출 보고서" diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json new file mode 100644 index 00000000000..38e81a83bfd --- /dev/null +++ b/apps/desktop/src/locales/lt/messages.json @@ -0,0 +1,2419 @@ +{ + "bitwarden": { + "message": "Bitwarden" + }, + "filters": { + "message": "Filters" + }, + "allItems": { + "message": "All items" + }, + "favorites": { + "message": "Favorites" + }, + "types": { + "message": "Types" + }, + "typeLogin": { + "message": "Login" + }, + "typeCard": { + "message": "Card" + }, + "typeIdentity": { + "message": "Identity" + }, + "typeSecureNote": { + "message": "Secure note" + }, + "folders": { + "message": "Folders" + }, + "collections": { + "message": "Collections" + }, + "searchVault": { + "message": "Search vault" + }, + "addItem": { + "message": "Add item" + }, + "shared": { + "message": "Shared" + }, + "share": { + "message": "Share" + }, + "moveToOrganization": { + "message": "Move to organization" + }, + "movedItemToOrg": { + "message": "$ITEMNAME$ moved to $ORGNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret Item" + }, + "orgname": { + "content": "$2", + "example": "Company Name" + } + } + }, + "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." + }, + "attachments": { + "message": "Attachments" + }, + "viewItem": { + "message": "View item" + }, + "name": { + "message": "Name" + }, + "uri": { + "message": "URI" + }, + "uriPosition": { + "message": "URI $POSITION$", + "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", + "placeholders": { + "position": { + "content": "$1", + "example": "2" + } + } + }, + "newUri": { + "message": "New URI" + }, + "username": { + "message": "Username" + }, + "password": { + "message": "Password" + }, + "passphrase": { + "message": "Passphrase" + }, + "editItem": { + "message": "Edit item" + }, + "emailAddress": { + "message": "Email address" + }, + "verificationCodeTotp": { + "message": "Verification code (TOTP)" + }, + "website": { + "message": "Website" + }, + "notes": { + "message": "Notes" + }, + "customFields": { + "message": "Custom fields" + }, + "launch": { + "message": "Launch" + }, + "copyValue": { + "message": "Copy value", + "description": "Copy value to clipboard" + }, + "minimizeOnCopyToClipboard": { + "message": "Minimize when copying to clipboard" + }, + "minimizeOnCopyToClipboardDesc": { + "message": "Minimize application when copying an item's data to the clipboard." + }, + "toggleVisibility": { + "message": "Toggle visibility" + }, + "toggleCollapse": { + "message": "Toggle collapse", + "description": "Toggling an expand/collapse state." + }, + "cardholderName": { + "message": "Cardholder name" + }, + "number": { + "message": "Number" + }, + "brand": { + "message": "Brand" + }, + "expiration": { + "message": "Expiration" + }, + "securityCode": { + "message": "Security code" + }, + "identityName": { + "message": "Identity name" + }, + "company": { + "message": "Company" + }, + "ssn": { + "message": "Social Security number" + }, + "passportNumber": { + "message": "Passport number" + }, + "licenseNumber": { + "message": "License number" + }, + "email": { + "message": "Email" + }, + "phone": { + "message": "Phone" + }, + "address": { + "message": "Address" + }, + "premiumRequired": { + "message": "Premium required" + }, + "premiumRequiredDesc": { + "message": "A Premium membership is required to use this feature." + }, + "errorOccurred": { + "message": "An error has occurred." + }, + "error": { + "message": "Error" + }, + "january": { + "message": "January" + }, + "february": { + "message": "February" + }, + "march": { + "message": "March" + }, + "april": { + "message": "April" + }, + "may": { + "message": "May" + }, + "june": { + "message": "June" + }, + "july": { + "message": "July" + }, + "august": { + "message": "August" + }, + "september": { + "message": "September" + }, + "october": { + "message": "October" + }, + "november": { + "message": "November" + }, + "december": { + "message": "December" + }, + "ex": { + "message": "ex.", + "description": "Short abbreviation for 'example'." + }, + "title": { + "message": "Title" + }, + "mr": { + "message": "Mr" + }, + "mrs": { + "message": "Mrs" + }, + "ms": { + "message": "Ms" + }, + "mx": { + "message": "Mx" + }, + "dr": { + "message": "Dr" + }, + "expirationMonth": { + "message": "Expiration month" + }, + "expirationYear": { + "message": "Expiration year" + }, + "select": { + "message": "Select" + }, + "other": { + "message": "Other" + }, + "generatePassword": { + "message": "Generate password" + }, + "type": { + "message": "Type" + }, + "firstName": { + "message": "First name" + }, + "middleName": { + "message": "Middle name" + }, + "lastName": { + "message": "Last name" + }, + "fullName": { + "message": "Full name" + }, + "address1": { + "message": "Address 1" + }, + "address2": { + "message": "Address 2" + }, + "address3": { + "message": "Address 3" + }, + "cityTown": { + "message": "City / Town" + }, + "stateProvince": { + "message": "State / Province" + }, + "zipPostalCode": { + "message": "Zip / Postal code" + }, + "country": { + "message": "Country" + }, + "save": { + "message": "Save" + }, + "cancel": { + "message": "Cancel" + }, + "delete": { + "message": "Delete" + }, + "favorite": { + "message": "Favorite" + }, + "edit": { + "message": "Edit" + }, + "authenticatorKeyTotp": { + "message": "Authenticator key (TOTP)" + }, + "folder": { + "message": "Folder" + }, + "newCustomField": { + "message": "New custom field" + }, + "value": { + "message": "Value" + }, + "dragToSort": { + "message": "Drag to sort" + }, + "cfTypeText": { + "message": "Text" + }, + "cfTypeHidden": { + "message": "Hidden" + }, + "cfTypeBoolean": { + "message": "Boolean" + }, + "cfTypeLinked": { + "message": "Linked", + "description": "This describes a field that is 'linked' (related) to another field." + }, + "linkedValue": { + "message": "Linked value", + "description": "This describes a value that is 'linked' (related) to another value." + }, + "remove": { + "message": "Remove" + }, + "nameRequired": { + "message": "Name is required." + }, + "addedItem": { + "message": "Item added" + }, + "editedItem": { + "message": "Item saved" + }, + "deleteItem": { + "message": "Delete item" + }, + "deleteFolder": { + "message": "Delete folder" + }, + "deleteAttachment": { + "message": "Delete attachment" + }, + "deleteItemConfirmation": { + "message": "Do you really want to send to the trash?" + }, + "deletedItem": { + "message": "Item sent to trash" + }, + "overwritePasswordConfirmation": { + "message": "Are you sure you want to overwrite the current password?" + }, + "overwriteUsername": { + "message": "Overwrite username" + }, + "overwriteUsernameConfirmation": { + "message": "Are you sure you want to overwrite the current username?" + }, + "noneFolder": { + "message": "No folder", + "description": "This is the folder for uncategorized items" + }, + "addFolder": { + "message": "Add folder" + }, + "editFolder": { + "message": "Edit folder" + }, + "regeneratePassword": { + "message": "Regenerate password" + }, + "copyPassword": { + "message": "Copy password" + }, + "copyUri": { + "message": "Copy URI" + }, + "copyVerificationCodeTotp": { + "message": "Copy verification code (TOTP)" + }, + "length": { + "message": "Length" + }, + "uppercase": { + "message": "Uppercase (A-Z)" + }, + "lowercase": { + "message": "Lowercase (a-z)" + }, + "numbers": { + "message": "Numbers (0-9)" + }, + "specialCharacters": { + "message": "Special characters (!@#$%^&*)" + }, + "numWords": { + "message": "Number of words" + }, + "wordSeparator": { + "message": "Word separator" + }, + "capitalize": { + "message": "Capitalize", + "description": "Make the first letter of a word uppercase." + }, + "includeNumber": { + "message": "Include number" + }, + "close": { + "message": "Close" + }, + "minNumbers": { + "message": "Minimum numbers" + }, + "minSpecial": { + "message": "Minimum special", + "description": "Minimum Special Characters" + }, + "ambiguous": { + "message": "Avoid ambiguous characters" + }, + "searchCollection": { + "message": "Search collection" + }, + "searchFolder": { + "message": "Search folder" + }, + "searchFavorites": { + "message": "Search favorites" + }, + "searchType": { + "message": "Search type", + "description": "Search item type" + }, + "newAttachment": { + "message": "Add new attachment" + }, + "deletedAttachment": { + "message": "Attachment deleted" + }, + "deleteAttachmentConfirmation": { + "message": "Are you sure you want to delete this attachment?" + }, + "attachmentSaved": { + "message": "Attachment saved" + }, + "file": { + "message": "File" + }, + "selectFile": { + "message": "Select a file" + }, + "maxFileSize": { + "message": "Maximum file size is 500 MB." + }, + "updateKey": { + "message": "You cannot use this feature until you update your encryption key." + }, + "editedFolder": { + "message": "Folder saved" + }, + "addedFolder": { + "message": "Folder added" + }, + "deleteFolderConfirmation": { + "message": "Are you sure you want to delete this folder?" + }, + "deletedFolder": { + "message": "Folder deleted" + }, + "loginOrCreateNewAccount": { + "message": "Log in or create a new account to access your secure vault." + }, + "createAccount": { + "message": "Create account" + }, + "logIn": { + "message": "Log in" + }, + "submit": { + "message": "Submit" + }, + "masterPass": { + "message": "Master password" + }, + "masterPassDesc": { + "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + }, + "masterPassHintDesc": { + "message": "A master password hint can help you remember your password if you forget it." + }, + "reTypeMasterPass": { + "message": "Re-type master password" + }, + "masterPassHint": { + "message": "Master password hint (optional)" + }, + "settings": { + "message": "Settings" + }, + "passwordHint": { + "message": "Password hint" + }, + "enterEmailToGetHint": { + "message": "Enter your account email address to receive your master password hint." + }, + "getMasterPasswordHint": { + "message": "Get master password hint" + }, + "emailRequired": { + "message": "Email address is required." + }, + "invalidEmail": { + "message": "Invalid email address." + }, + "masterPasswordRequired": { + "message": "Master password is required." + }, + "confirmMasterPasswordRequired": { + "message": "Master password retype is required." + }, + "masterPasswordMinlength": { + "message": "Master password must be at least $VALUE$ characters long.", + "description": "The Master Password must be at least a specific number of characters long.", + "placeholders": { + "value": { + "content": "$1", + "example": "8" + } + } + }, + "masterPassDoesntMatch": { + "message": "Master password confirmation does not match." + }, + "newAccountCreated": { + "message": "Your new account has been created! You may now log in." + }, + "masterPassSent": { + "message": "We've sent you an email with your master password hint." + }, + "unexpectedError": { + "message": "An unexpected error has occurred." + }, + "itemInformation": { + "message": "Item information" + }, + "noItemsInList": { + "message": "There are no items to list." + }, + "sendVerificationCode": { + "message": "Send a verification code to your email" + }, + "sendCode": { + "message": "Send code" + }, + "codeSent": { + "message": "Code sent" + }, + "verificationCode": { + "message": "Verification code" + }, + "confirmIdentity": { + "message": "Confirm your identity to continue." + }, + "verificationCodeRequired": { + "message": "Verification code is required." + }, + "invalidVerificationCode": { + "message": "Invalid verification code" + }, + "continue": { + "message": "Continue" + }, + "enterVerificationCodeApp": { + "message": "Enter the 6 digit verification code from your authenticator app." + }, + "enterVerificationCodeEmail": { + "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "verificationCodeEmailSent": { + "message": "Verification email sent to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "rememberMe": { + "message": "Remember me" + }, + "sendVerificationCodeEmailAgain": { + "message": "Send verification code email again" + }, + "useAnotherTwoStepMethod": { + "message": "Use another two-step login method" + }, + "insertYubiKey": { + "message": "Insert your YubiKey into your computer's USB port, then touch its button." + }, + "insertU2f": { + "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + }, + "recoveryCodeDesc": { + "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers on your account." + }, + "recoveryCodeTitle": { + "message": "Recovery code" + }, + "authenticatorAppTitle": { + "message": "Authenticator app" + }, + "authenticatorAppDesc": { + "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." + }, + "yubiKeyTitle": { + "message": "YubiKey OTP security key" + }, + "yubiKeyDesc": { + "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + }, + "duoDesc": { + "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." + }, + "duoOrganizationDesc": { + "message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", + "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." + }, + "webAuthnTitle": { + "message": "FIDO2 WebAuthn" + }, + "webAuthnDesc": { + "message": "Use any WebAuthn compatible security key to access your account." + }, + "emailTitle": { + "message": "Email" + }, + "emailDesc": { + "message": "Verification codes will be emailed to you." + }, + "loginUnavailable": { + "message": "Login unavailable" + }, + "noTwoStepProviders": { + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this device." + }, + "noTwoStepProviders2": { + "message": "Please add additional providers that are better supported across devices (such as an authenticator app)." + }, + "twoStepOptions": { + "message": "Two-step login options" + }, + "selfHostedEnvironment": { + "message": "Self-hosted environment" + }, + "selfHostedEnvironmentFooter": { + "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + }, + "customEnvironment": { + "message": "Custom environment" + }, + "customEnvironmentFooter": { + "message": "For advanced users. You can specify the base URL of each service independently." + }, + "baseUrl": { + "message": "Server URL" + }, + "apiUrl": { + "message": "API server URL" + }, + "webVaultUrl": { + "message": "Web vault server URL" + }, + "identityUrl": { + "message": "Identity server URL" + }, + "notificationsUrl": { + "message": "Notifications server URL" + }, + "iconsUrl": { + "message": "Icons server URL" + }, + "environmentSaved": { + "message": "Environment URLs saved" + }, + "ok": { + "message": "Ok" + }, + "yes": { + "message": "Yes" + }, + "no": { + "message": "No" + }, + "overwritePassword": { + "message": "Overwrite password" + }, + "learnMore": { + "message": "Learn more" + }, + "featureUnavailable": { + "message": "Feature unavailable" + }, + "loggedOut": { + "message": "Logged out" + }, + "loginExpired": { + "message": "Your login session has expired." + }, + "logOutConfirmation": { + "message": "Are you sure you want to log out?" + }, + "logOut": { + "message": "Log out" + }, + "addNewLogin": { + "message": "New login" + }, + "addNewItem": { + "message": "New item" + }, + "addNewFolder": { + "message": "New folder" + }, + "view": { + "message": "View" + }, + "account": { + "message": "Account" + }, + "loading": { + "message": "Loading..." + }, + "lockVault": { + "message": "Lock vault" + }, + "passwordGenerator": { + "message": "Password generator" + }, + "contactUs": { + "message": "Contact us" + }, + "helpAndFeedback": { + "message": "Help and feedback" + }, + "getHelp": { + "message": "Get help" + }, + "fileBugReport": { + "message": "File a bug report" + }, + "blog": { + "message": "Blog" + }, + "followUs": { + "message": "Follow us" + }, + "syncVault": { + "message": "Sync vault" + }, + "changeMasterPass": { + "message": "Change master password" + }, + "changeMasterPasswordConfirmation": { + "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "fingerprintPhrase": { + "message": "Fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "yourAccountsFingerprint": { + "message": "Your account's fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "goToWebVault": { + "message": "Go to web vault" + }, + "getMobileApp": { + "message": "Get mobile app" + }, + "getBrowserExtension": { + "message": "Get browser extension" + }, + "syncingComplete": { + "message": "Syncing complete" + }, + "syncingFailed": { + "message": "Syncing failed" + }, + "yourVaultIsLocked": { + "message": "Your vault is locked. Verify your identity to continue." + }, + "unlock": { + "message": "Unlock" + }, + "loggedInAsOn": { + "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + }, + "hostname": { + "content": "$2", + "example": "bitwarden.com" + } + } + }, + "invalidMasterPassword": { + "message": "Invalid master password" + }, + "twoStepLoginConfirmation": { + "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "twoStepLogin": { + "message": "Two-step login" + }, + "vaultTimeout": { + "message": "Vault timeout" + }, + "vaultTimeoutDesc": { + "message": "Choose when your vault will take the vault timeout action." + }, + "immediately": { + "message": "Immediately" + }, + "tenSeconds": { + "message": "10 seconds" + }, + "twentySeconds": { + "message": "20 seconds" + }, + "thirtySeconds": { + "message": "30 seconds" + }, + "oneMinute": { + "message": "1 minute" + }, + "twoMinutes": { + "message": "2 minutes" + }, + "fiveMinutes": { + "message": "5 minutes" + }, + "fifteenMinutes": { + "message": "15 minutes" + }, + "thirtyMinutes": { + "message": "30 minutes" + }, + "oneHour": { + "message": "1 hour" + }, + "fourHours": { + "message": "4 hours" + }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, + "onLocked": { + "message": "On system lock" + }, + "onRestart": { + "message": "On restart" + }, + "never": { + "message": "Never" + }, + "security": { + "message": "Security" + }, + "clearClipboard": { + "message": "Clear clipboard", + "description": "Clipboard is the operating system thing where you copy/paste data to on your device." + }, + "clearClipboardDesc": { + "message": "Automatically clear copied values from your clipboard.", + "description": "Clipboard is the operating system thing where you copy/paste data to on your device." + }, + "enableFavicon": { + "message": "Show website icons" + }, + "faviconDesc": { + "message": "Show a recognizable image next to each login." + }, + "enableMinToTray": { + "message": "Minimize to tray icon" + }, + "enableMinToTrayDesc": { + "message": "When minimizing the window, show an icon in the system tray instead." + }, + "enableMinToMenuBar": { + "message": "Minimize to menu bar" + }, + "enableMinToMenuBarDesc": { + "message": "When minimizing the window, show an icon in the menu bar instead." + }, + "enableCloseToTray": { + "message": "Close to tray icon" + }, + "enableCloseToTrayDesc": { + "message": "When closing the window, show an icon in the system tray instead." + }, + "enableCloseToMenuBar": { + "message": "Close to menu bar" + }, + "enableCloseToMenuBarDesc": { + "message": "When closing the window, show an icon in the menu bar instead." + }, + "enableTray": { + "message": "Show tray icon" + }, + "enableTrayDesc": { + "message": "Always show an icon in the system tray." + }, + "startToTray": { + "message": "Start to tray icon" + }, + "startToTrayDesc": { + "message": "When the application is first started, only show an icon in the system tray." + }, + "startToMenuBar": { + "message": "Start to menu bar" + }, + "startToMenuBarDesc": { + "message": "When the application is first started, only show an icon in the menu bar." + }, + "openAtLogin": { + "message": "Start automatically on login" + }, + "openAtLoginDesc": { + "message": "Start the Bitwarden desktop application automatically on login." + }, + "alwaysShowDock": { + "message": "Always show in the Dock" + }, + "alwaysShowDockDesc": { + "message": "Show the Bitwarden icon in the Dock even when minimized to the menu bar." + }, + "confirmTrayTitle": { + "message": "Confirm hiding tray" + }, + "confirmTrayDesc": { + "message": "Turning off this setting will also turn off all other tray related settings." + }, + "language": { + "message": "Language" + }, + "languageDesc": { + "message": "Change the language used by the application. Restart is required." + }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Change the application's color theme." + }, + "dark": { + "message": "Dark", + "description": "Dark color" + }, + "light": { + "message": "Light", + "description": "Light color" + }, + "copy": { + "message": "Copy", + "description": "Copy to clipboard" + }, + "checkForUpdates": { + "message": "Check for updates…" + }, + "version": { + "message": "Version $VERSION_NUM$", + "placeholders": { + "version_num": { + "content": "$1", + "example": "1.2.3" + } + } + }, + "restartToUpdate": { + "message": "Restart to update" + }, + "restartToUpdateDesc": { + "message": "Version $VERSION_NUM$ is ready to install. You must restart the application to complete the installation. Do you want to restart and update now?", + "placeholders": { + "version_num": { + "content": "$1", + "example": "1.2.3" + } + } + }, + "updateAvailable": { + "message": "Update available" + }, + "updateAvailableDesc": { + "message": "An update was found. Do you want to download it now?" + }, + "restart": { + "message": "Restart" + }, + "later": { + "message": "Later" + }, + "noUpdatesAvailable": { + "message": "No updates are currently available. You are using the latest version." + }, + "updateError": { + "message": "Update error" + }, + "unknown": { + "message": "Unknown" + }, + "copyUsername": { + "message": "Copy username" + }, + "copyNumber": { + "message": "Copy number", + "description": "Copy credit card number" + }, + "copySecurityCode": { + "message": "Copy security code", + "description": "Copy credit card security code (CVV)" + }, + "premiumMembership": { + "message": "Premium membership" + }, + "premiumManage": { + "message": "Manage membership" + }, + "premiumManageAlert": { + "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumRefresh": { + "message": "Refresh membership" + }, + "premiumNotCurrentMember": { + "message": "You are not currently a Premium member." + }, + "premiumSignUpAndGet": { + "message": "Sign up for a Premium membership and get:" + }, + "premiumSignUpStorage": { + "message": "1 GB encrypted storage for file attachments." + }, + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." + }, + "premiumSignUpReports": { + "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + }, + "premiumSignUpTotp": { + "message": "TOTP verification code (2FA) generator for logins in your vault." + }, + "premiumSignUpSupport": { + "message": "Priority customer support." + }, + "premiumSignUpFuture": { + "message": "All future premium features. More coming soon!" + }, + "premiumPurchase": { + "message": "Purchase Premium" + }, + "premiumPurchaseAlert": { + "message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumCurrentMember": { + "message": "You are a premium member!" + }, + "premiumCurrentMemberThanks": { + "message": "Thank you for supporting Bitwarden." + }, + "premiumPrice": { + "message": "All for just $PRICE$ /year!", + "placeholders": { + "price": { + "content": "$1", + "example": "$10" + } + } + }, + "refreshComplete": { + "message": "Refresh complete" + }, + "passwordHistory": { + "message": "Password history" + }, + "clear": { + "message": "Clear", + "description": "To clear something out. example: To clear browser history." + }, + "noPasswordsInList": { + "message": "There are no passwords to list." + }, + "undo": { + "message": "Undo" + }, + "redo": { + "message": "Redo" + }, + "cut": { + "message": "Cut", + "description": "Cut to clipboard" + }, + "paste": { + "message": "Paste", + "description": "Paste from clipboard" + }, + "selectAll": { + "message": "Select all" + }, + "zoomIn": { + "message": "Zoom in" + }, + "zoomOut": { + "message": "Zoom out" + }, + "resetZoom": { + "message": "Reset zoom" + }, + "toggleFullScreen": { + "message": "Toggle full screen" + }, + "reload": { + "message": "Reload" + }, + "toggleDevTools": { + "message": "Toggle developer tools" + }, + "minimize": { + "message": "Minimize", + "description": "Minimize window" + }, + "zoom": { + "message": "Zoom" + }, + "bringAllToFront": { + "message": "Bring all to front", + "description": "Bring all windows to front (foreground)" + }, + "aboutBitwarden": { + "message": "About Bitwarden" + }, + "services": { + "message": "Services" + }, + "hideBitwarden": { + "message": "Hide Bitwarden" + }, + "hideOthers": { + "message": "Hide others" + }, + "showAll": { + "message": "Show all" + }, + "quitBitwarden": { + "message": "Quit Bitwarden" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } + }, + "help": { + "message": "Help" + }, + "window": { + "message": "Window" + }, + "checkPassword": { + "message": "Check if password has been exposed." + }, + "passwordExposed": { + "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "placeholders": { + "value": { + "content": "$1", + "example": "2" + } + } + }, + "passwordSafe": { + "message": "This password was not found in any known data breaches. It should be safe to use." + }, + "baseDomain": { + "message": "Base domain", + "description": "Domain name. Ex. website.com" + }, + "domainName": { + "message": "Domain name", + "description": "Domain name. Ex. website.com" + }, + "host": { + "message": "Host", + "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." + }, + "exact": { + "message": "Exact" + }, + "startsWith": { + "message": "Starts with" + }, + "regEx": { + "message": "Regular expression", + "description": "A programming term, also known as 'RegEx'." + }, + "matchDetection": { + "message": "Match detection", + "description": "URI match detection for auto-fill." + }, + "defaultMatchDetection": { + "message": "Default match detection", + "description": "Default URI match detection for auto-fill." + }, + "toggleOptions": { + "message": "Toggle options" + }, + "organization": { + "message": "Organization", + "description": "An entity of multiple related people (ex. a team or business organization)." + }, + "default": { + "message": "Default" + }, + "exit": { + "message": "Exit" + }, + "showHide": { + "message": "Show / Hide", + "description": "Text for a button that toggles the visibility of the window. Shows the window when it is hidden or hides the window if it is currently open." + }, + "hideToTray": { + "message": "Hide to tray" + }, + "alwaysOnTop": { + "message": "Always on top", + "description": "Application window should always stay on top of other windows" + }, + "dateUpdated": { + "message": "Updated", + "description": "ex. Date this item was updated" + }, + "dateCreated": { + "message": "Created", + "description": "ex. Date this item was created" + }, + "datePasswordUpdated": { + "message": "Password updated", + "description": "ex. Date this password was updated" + }, + "exportVault": { + "message": "Export vault" + }, + "fileFormat": { + "message": "File format" + }, + "hCaptchaUrl": { + "message": "hCaptcha Url", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "loadAccessibilityCookie": { + "message": "Load accessibility cookie" + }, + "registerAccessibilityUser": { + "message": "Register as an accessibility user at", + "description": "ex. Register as an accessibility user at hcaptcha.com" + }, + "copyPasteLink": { + "message": "Copy and paste the link sent to your email below" + }, + "enterhCaptchaUrl": { + "message": "Enter URL to load accessibility cookie for hCaptcha", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "hCaptchaUrlRequired": { + "message": "hCaptcha Url is required", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "invalidUrl": { + "message": "Invalid Url" + }, + "done": { + "message": "Done" + }, + "accessibilityCookieSaved": { + "message": "Accessibility cookie saved!" + }, + "noAccessibilityCookieSaved": { + "message": "No accessibility cookie saved" + }, + "warning": { + "message": "WARNING", + "description": "WARNING (should stay in capitalized letters if the language permits)" + }, + "confirmVaultExport": { + "message": "Confirm vault export" + }, + "exportWarningDesc": { + "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it." + }, + "encExportKeyWarningDesc": { + "message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file." + }, + "encExportAccountWarningDesc": { + "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + }, + "noOrganizationsList": { + "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + }, + "noCollectionsInList": { + "message": "There are no collections to list." + }, + "ownership": { + "message": "Ownership" + }, + "whoOwnsThisItem": { + "message": "Who owns this item?" + }, + "strong": { + "message": "Strong", + "description": "ex. A strong password. Scale: Weak -> Good -> Strong" + }, + "good": { + "message": "Good", + "description": "ex. A good password. Scale: Weak -> Good -> Strong" + }, + "weak": { + "message": "Weak", + "description": "ex. A weak password. Scale: Weak -> Good -> Strong" + }, + "weakMasterPassword": { + "message": "Weak master password" + }, + "weakMasterPasswordDesc": { + "message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?" + }, + "pin": { + "message": "PIN", + "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." + }, + "unlockWithPin": { + "message": "Unlock with PIN" + }, + "setYourPinCode": { + "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." + }, + "pinRequired": { + "message": "PIN code is required." + }, + "invalidPin": { + "message": "Invalid PIN code." + }, + "unlockWithWindowsHello": { + "message": "Unlock with Windows Hello" + }, + "additionalWindowsHelloSettings": { + "message": "Additional Windows Hello settings" + }, + "windowsHelloConsentMessage": { + "message": "Verify for Bitwarden." + }, + "unlockWithTouchId": { + "message": "Unlock with Touch ID" + }, + "additionalTouchIdSettings": { + "message": "Additional Touch ID settings" + }, + "touchIdConsentMessage": { + "message": "unlock your vault" + }, + "autoPromptWindowsHello": { + "message": "Ask for Windows Hello on app start" + }, + "autoPromptTouchId": { + "message": "Ask for Touch ID on app start" + }, + "requirePasswordOnStart": { + "message": "Require password or PIN on app start" + }, + "recommendedForSecurity": { + "message": "Recommended for security." + }, + "lockWithMasterPassOnRestart": { + "message": "Lock with master password on restart" + }, + "deleteAccount": { + "message": "Delete account" + }, + "deleteAccountDesc": { + "message": "Proceed below to delete your account and all vault data." + }, + "deleteAccountWarning": { + "message": "Deleting your account is permanent. It cannot be undone." + }, + "accountDeleted": { + "message": "Account deleted" + }, + "accountDeletedDesc": { + "message": "Your account has been closed and all associated data has been deleted." + }, + "preferences": { + "message": "Preferences" + }, + "enableMenuBar": { + "message": "Show menu bar icon" + }, + "enableMenuBarDesc": { + "message": "Always show an icon in the menu bar." + }, + "hideToMenuBar": { + "message": "Hide to menu bar" + }, + "selectOneCollection": { + "message": "You must select at least one collection." + }, + "premiumUpdated": { + "message": "You've upgraded to Premium." + }, + "restore": { + "message": "Restore" + }, + "premiumManageAlertAppStore": { + "message": "You can manage your subscription from the App Store. Do you want to visit the App Store now?" + }, + "legal": { + "message": "Legal", + "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." + }, + "termsOfService": { + "message": "Terms of Service" + }, + "privacyPolicy": { + "message": "Privacy Policy" + }, + "unsavedChangesConfirmation": { + "message": "Are you sure you want to leave? If you leave now then your current information will not be saved." + }, + "unsavedChangesTitle": { + "message": "Unsaved changes" + }, + "clone": { + "message": "Clone" + }, + "passwordGeneratorPolicyInEffect": { + "message": "One or more organization policies are affecting your generator settings." + }, + "vaultTimeoutAction": { + "message": "Vault timeout action" + }, + "vaultTimeoutActionLockDesc": { + "message": "Master password or other unlock method is required to access your vault again." + }, + "vaultTimeoutActionLogOutDesc": { + "message": "Re-authentication is required to access your vault again." + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "lock": { + "message": "Lock", + "description": "Verb form: to make secure or inaccesible by" + }, + "trash": { + "message": "Trash", + "description": "Noun: a special folder to hold deleted items" + }, + "searchTrash": { + "message": "Search trash" + }, + "permanentlyDeleteItem": { + "message": "Permanently delete item" + }, + "permanentlyDeleteItemConfirmation": { + "message": "Are you sure you want to permanently delete this item?" + }, + "permanentlyDeletedItem": { + "message": "Item permanently deleted" + }, + "restoredItem": { + "message": "Item restored" + }, + "permanentlyDelete": { + "message": "Permanently delete" + }, + "vaultTimeoutLogOutConfirmation": { + "message": "Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?" + }, + "vaultTimeoutLogOutConfirmationTitle": { + "message": "Timeout action confirmation" + }, + "enterpriseSingleSignOn": { + "message": "Enterprise single sign-on" + }, + "setMasterPassword": { + "message": "Set master password" + }, + "ssoCompleteRegistration": { + "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." + }, + "currentMasterPass": { + "message": "Current master password" + }, + "newMasterPass": { + "message": "New master password" + }, + "confirmNewMasterPass": { + "message": "Confirm new master password" + }, + "masterPasswordPolicyInEffect": { + "message": "One or more organization policies require your master password to meet the following requirements:" + }, + "policyInEffectMinComplexity": { + "message": "Minimum complexity score of $SCORE$", + "placeholders": { + "score": { + "content": "$1", + "example": "4" + } + } + }, + "policyInEffectMinLength": { + "message": "Minimum length of $LENGTH$", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "policyInEffectUppercase": { + "message": "Contain one or more uppercase characters" + }, + "policyInEffectLowercase": { + "message": "Contain one or more lowercase characters" + }, + "policyInEffectNumbers": { + "message": "Contain one or more numbers" + }, + "policyInEffectSpecial": { + "message": "Contain one or more of the following special characters $CHARS$", + "placeholders": { + "chars": { + "content": "$1", + "example": "!@#$%^&*" + } + } + }, + "masterPasswordPolicyRequirementsNotMet": { + "message": "Your new master password does not meet the policy requirements." + }, + "acceptPolicies": { + "message": "By checking this box you agree to the following:" + }, + "acceptPoliciesRequired": { + "message": "Terms of Service and Privacy Policy have not been acknowledged." + }, + "enableBrowserIntegration": { + "message": "Allow browser integration" + }, + "enableBrowserIntegrationDesc": { + "message": "Used for biometrics in browser." + }, + "enableDuckDuckGoBrowserIntegration": { + "message": "Allow DuckDuckGo browser integration" + }, + "enableDuckDuckGoBrowserIntegrationDesc": { + "message": "Use your Bitwarden vault when browsing with DuckDuckGo." + }, + "browserIntegrationUnsupportedTitle": { + "message": "Browser integration not supported" + }, + "browserIntegrationMasOnlyDesc": { + "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." + }, + "browserIntegrationWindowsStoreDesc": { + "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." + }, + "browserIntegrationLinuxDesc": { + "message": "Unfortunately browser integration is currently not supported in the linux version." + }, + "enableBrowserIntegrationFingerprint": { + "message": "Require verification for browser integration" + }, + "enableBrowserIntegrationFingerprintDesc": { + "message": "Add an additional layer of security by requiring fingerprint phrase confirmation when establishing a link between your desktop and browser. This requires user action and verification each time a connection is created." + }, + "approve": { + "message": "Approve" + }, + "verifyBrowserTitle": { + "message": "Verify browser connection" + }, + "verifyBrowserDesc": { + "message": "Please ensure the shown fingerprint is identical to the fingerprint showed in the browser extension." + }, + "verifyNativeMessagingConnectionTitle": { + "message": "$APPID$ wants to connect to Bitwarden", + "placeholders": { + "appid": { + "content": "$1", + "example": "My App" + } + } + }, + "verifyNativeMessagingConnectionDesc": { + "message": "Would you like to approve this request?" + }, + "verifyNativeMessagingConnectionWarning": { + "message": "If you did not initiate this request, do not approve it." + }, + "biometricsNotEnabledTitle": { + "message": "Biometrics not set up" + }, + "biometricsNotEnabledDesc": { + "message": "Browser biometrics requires desktop biometrics to be set up in the settings first." + }, + "personalOwnershipSubmitError": { + "message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." + }, + "hintEqualsPassword": { + "message": "Your password hint cannot be the same as your password." + }, + "personalOwnershipPolicyInEffect": { + "message": "An organization policy is affecting your ownership options." + }, + "allSends": { + "message": "All Sends", + "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeFile": { + "message": "File" + }, + "sendTypeText": { + "message": "Text" + }, + "searchSends": { + "message": "Search Sends", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editSend": { + "message": "Edit Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "myVault": { + "message": "My vault" + }, + "text": { + "message": "Text" + }, + "deletionDate": { + "message": "Deletion date" + }, + "deletionDateDesc": { + "message": "The Send will be permanently deleted on the specified date and time.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "expirationDate": { + "message": "Expiration date" + }, + "expirationDateDesc": { + "message": "If set, access to this Send will expire on the specified date and time.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "maxAccessCount": { + "message": "Maximum access count", + "description": "This text will be displayed after a Send has been accessed the maximum amount of times." + }, + "maxAccessCountDesc": { + "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "currentAccessCount": { + "message": "Current access count" + }, + "disableSend": { + "message": "Deactivate this Send so that no one can access it.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDesc": { + "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." + }, + "sendNotesDesc": { + "message": "Private notes about this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendLink": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendLinkLabel": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "textHiddenByDefault": { + "message": "When accessing the Send, hide the text by default", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "createdSend": { + "message": "Send added", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editedSend": { + "message": "Send saved", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "deletedSend": { + "message": "Send deleted", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "newPassword": { + "message": "New password" + }, + "whatTypeOfSend": { + "message": "What type of Send is this?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "createSend": { + "message": "New Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTextDesc": { + "message": "The text you want to send." + }, + "sendFileDesc": { + "message": "The file you want to send." + }, + "days": { + "message": "$DAYS$ days", + "placeholders": { + "days": { + "content": "$1", + "example": "1" + } + } + }, + "oneDay": { + "message": "1 day" + }, + "custom": { + "message": "Custom" + }, + "deleteSendConfirmation": { + "message": "Are you sure you want to delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copySendLinkToClipboard": { + "message": "Copy Send link to clipboard", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copySendLinkOnSave": { + "message": "Copy the link to share this Send to my clipboard upon save." + }, + "sendDisabled": { + "message": "Send removed", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendDisabledWarning": { + "message": "Due to an enterprise policy, you are only able to delete an existing Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copyLink": { + "message": "Copy link" + }, + "disabled": { + "message": "Disabled" + }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, + "maxAccessCountReached": { + "message": "Max access count reached" + }, + "expired": { + "message": "Expired" + }, + "pendingDeletion": { + "message": "Pending deletion" + }, + "webAuthnAuthenticate": { + "message": "Authenticate WebAuthn" + }, + "hideEmail": { + "message": "Hide my email address from recipients." + }, + "sendOptionsPolicyInEffect": { + "message": "One or more organization policies are affecting your Send options." + }, + "emailVerificationRequired": { + "message": "Email verification required" + }, + "emailVerificationRequiredDesc": { + "message": "You must verify your email to use this feature." + }, + "passwordPrompt": { + "message": "Master password re-prompt" + }, + "passwordConfirmation": { + "message": "Master password confirmation" + }, + "passwordConfirmationDesc": { + "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + }, + "updatedMasterPassword": { + "message": "Updated master password" + }, + "updateMasterPassword": { + "message": "Update master password" + }, + "updateMasterPasswordWarning": { + "message": "Your master password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + }, + "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." + }, + "hours": { + "message": "Hours" + }, + "minutes": { + "message": "Minutes" + }, + "vaultTimeoutPolicyInEffect": { + "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "vaultTimeoutPolicyWithActionInEffect": { + "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + }, + "action": { + "content": "$3", + "example": "Lock" + } + } + }, + "vaultTimeoutActionPolicyInEffect": { + "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "placeholders": { + "action": { + "content": "$1", + "example": "Lock" + } + } + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "resetPasswordPolicyAutoEnroll": { + "message": "Automatic enrollment" + }, + "resetPasswordAutoEnrollInviteWarning": { + "message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password." + }, + "vaultExportDisabled": { + "message": "Vault export removed" + }, + "personalVaultExportPolicyInEffect": { + "message": "One or more organization policies prevents you from exporting your personal vault." + }, + "addAccount": { + "message": "Add account" + }, + "removeMasterPassword": { + "message": "Remove master password" + }, + "removedMasterPassword": { + "message": "Master password removed" + }, + "convertOrganizationEncryptionDesc": { + "message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "leaveOrganization": { + "message": "Leave organization" + }, + "leaveOrganizationConfirmation": { + "message": "Are you sure you want to leave this organization?" + }, + "leftOrganization": { + "message": "You have left the organization." + }, + "ssoKeyConnectorError": { + "message": "Key connector error: make sure key connector is available and working correctly." + }, + "lockAllVaults": { + "message": "Lock all vaults" + }, + "accountLimitReached": { + "message": "No more than 5 accounts may be logged in at the same time." + }, + "accountPreferences": { + "message": "Preferences" + }, + "appPreferences": { + "message": "App settings (all accounts)" + }, + "accountSwitcherLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "settingsTitle": { + "message": "App settings for $EMAIL$", + "placeholders": { + "email": { + "content": "$1", + "example": "jdoe@example.com" + } + } + }, + "switchAccount": { + "message": "Switch account" + }, + "options": { + "message": "Options" + }, + "sessionTimeout": { + "message": "Your session has timed out. Please go back and try logging in again." + }, + "exportingPersonalVaultTitle": { + "message": "Exporting individual vault" + }, + "exportingPersonalVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "locked": { + "message": "Locked" + }, + "unlocked": { + "message": "Unlocked" + }, + "generator": { + "message": "Generator" + }, + "whatWouldYouLikeToGenerate": { + "message": "What would you like to generate?" + }, + "passwordType": { + "message": "Password type" + }, + "regenerateUsername": { + "message": "Regenerate username" + }, + "generateUsername": { + "message": "Generate username" + }, + "usernameType": { + "message": "Username type" + }, + "plusAddressedEmail": { + "message": "Plus addressed email", + "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" + }, + "plusAddressedEmailDesc": { + "message": "Use your email provider's sub-addressing capabilities." + }, + "catchallEmail": { + "message": "Catch-all email" + }, + "catchallEmailDesc": { + "message": "Use your domain's configured catch-all inbox." + }, + "random": { + "message": "Random" + }, + "randomWord": { + "message": "Random word" + }, + "websiteName": { + "message": "Website name" + }, + "service": { + "message": "Service" + }, + "allVaults": { + "message": "All vaults" + }, + "searchOrganization": { + "message": "Search organization" + }, + "searchMyVault": { + "message": "Search my vault" + }, + "forwardedEmail": { + "message": "Forwarded email alias" + }, + "forwardedEmailDesc": { + "message": "Generate an email alias with an external forwarding service." + }, + "hostname": { + "message": "Hostname", + "description": "Part of a URL." + }, + "apiAccessToken": { + "message": "API Access Token" + }, + "apiKey": { + "message": "API key" + }, + "premiumSubcriptionRequired": { + "message": "Premium subscription required" + }, + "organizationIsDisabled": { + "message": "Organization suspended" + }, + "disabledOrganizationFilterError": { + "message": "Items in suspended organizations cannot be accessed. Contact your organization owner for assistance." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "vault": { + "message": "Vault" + }, + "loginWithMasterPassword": { + "message": "Log in with master password" + }, + "loggingInAs": { + "message": "Logging in as" + }, + "rememberEmail": { + "message": "Remember email" + }, + "notYou": { + "message": "Not you?" + }, + "newAroundHere": { + "message": "New around here?" + }, + "loggingInTo": { + "message": "Logging in to $DOMAIN$", + "placeholders": { + "domain": { + "content": "$1", + "example": "example.com" + } + } + }, + "logInWithAnotherDevice": { + "message": "Log in with another device" + }, + "loginInitiated": { + "message": "Login initiated" + }, + "notificationSentDevice": { + "message": "A notification has been sent to your device." + }, + "fingerprintMatchInfo": { + "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." + }, + "fingerprintPhraseHeader": { + "message": "Fingerprint phrase" + }, + "needAnotherOption": { + "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" + }, + "viewAllLoginOptions": { + "message": "View all login options" + }, + "resendNotification": { + "message": "Resend notification" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." + }, + "areYouTryingtoLogin": { + "message": "Are you trying to log in?" + }, + "logInAttemptBy": { + "message": "Login attempt by $EMAIL$", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "deviceType": { + "message": "Device Type" + }, + "ipAddress": { + "message": "IP Address" + }, + "time": { + "message": "Time" + }, + "confirmLogIn": { + "message": "Confirm login" + }, + "denyLogIn": { + "message": "Deny login" + }, + "approveLoginRequests": { + "message": "Approve login requests" + }, + "logInConfirmedForEmailOnDevice": { + "message": "Login confirmed for $EMAIL$ on $DEVICE$", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + }, + "device": { + "content": "$2", + "example": "iOS" + } + } + }, + "youDeniedALogInAttemptFromAnotherDevice": { + "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again." + }, + "justNow": { + "message": "Just now" + }, + "requestedXMinutesAgo": { + "message": "Requested $MINUTES$ minutes ago", + "placeholders": { + "minutes": { + "content": "$1", + "example": "5" + } + } + }, + "loginRequestHasAlreadyExpired": { + "message": "Login request has already expired." + }, + "thisRequestIsNoLongerValid": { + "message": "This request is no longer valid." + }, + "approveLoginRequestDesc": { + "message": "Use this device to approve login requests made from other devices." + }, + "confirmLoginAtemptForMail": { + "message": "Confirm login attempt for $EMAIL$", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "logInRequested": { + "message": "Log in requested" + }, + "exposedMasterPassword": { + "message": "Exposed Master Password" + }, + "exposedMasterPasswordDesc": { + "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + }, + "weakAndExposedMasterPassword": { + "message": "Weak and Exposed Master Password" + }, + "weakAndBreachedMasterPasswordDesc": { + "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + }, + "checkForBreaches": { + "message": "Check known data breaches for this password" + }, + "important": { + "message": "Important:" + }, + "masterPasswordHint": { + "message": "Your master password cannot be recovered if you forget it!" + }, + "characterMinimum": { + "message": "$LENGTH$ character minimum", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "windowsBiometricUpdateWarning": { + "message": "Bitwarden recommends updating your biometric settings to require your master password (or PIN) on the first unlock. Would you like to update your settings now?" + }, + "windowsBiometricUpdateWarningTitle": { + "message": "Recommended Settings Update" + }, + "deviceApprovalRequired": { + "message": "Device approval required. Select an approval option below:" + }, + "rememberThisDevice": { + "message": "Remember this device" + }, + "uncheckIfPublicDevice": { + "message": "Uncheck if using a public device" + }, + "approveFromYourOtherDevice": { + "message": "Approve from your other device" + }, + "requestAdminApproval": { + "message": "Request admin approval" + }, + "approveWithMasterPassword": { + "message": "Approve with master password" + }, + "region": { + "message": "Region" + }, + "ssoIdentifierRequired": { + "message": "Organization SSO identifier is required." + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "loggingInOn": { + "message": "Logging in on" + }, + "usDomain": { + "message": "bitwarden.com" + }, + "euDomain": { + "message": "bitwarden.eu" + }, + "selfHosted": { + "message": "Self-hosted" + }, + "accessDenied": { + "message": "Access denied. You do not have permission to view this page." + }, + "accountSuccessfullyCreated": { + "message": "Account successfully created!" + }, + "adminApprovalRequested": { + "message": "Admin approval requested" + }, + "adminApprovalRequestSentToAdmins": { + "message": "Your request has been sent to your admin." + }, + "youWillBeNotifiedOnceApproved": { + "message": "You will be notified once approved." + }, + "troubleLoggingIn": { + "message": "Trouble logging in?" + }, + "loginApproved": { + "message": "Login approved" + }, + "userEmailMissing": { + "message": "User email missing" + }, + "deviceTrusted": { + "message": "Device trusted" + }, + "inputRequired": { + "message": "Input is required." + }, + "required": { + "message": "required" + }, + "search": { + "message": "Search" + }, + "inputMinLength": { + "message": "Input must be at least $COUNT$ characters long.", + "placeholders": { + "count": { + "content": "$1", + "example": "8" + } + } + }, + "inputMaxLength": { + "message": "Input must not exceed $COUNT$ characters in length.", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "inputForbiddenCharacters": { + "message": "The following characters are not allowed: $CHARACTERS$", + "placeholders": { + "characters": { + "content": "$1", + "example": "@, #, $, %" + } + } + }, + "inputMinValue": { + "message": "Input value must be at least $MIN$.", + "placeholders": { + "min": { + "content": "$1", + "example": "8" + } + } + }, + "inputMaxValue": { + "message": "Input value must not exceed $MAX$.", + "placeholders": { + "max": { + "content": "$1", + "example": "100" + } + } + }, + "multipleInputEmails": { + "message": "1 or more emails are invalid" + }, + "inputTrimValidator": { + "message": "Input must not contain only whitespace.", + "description": "Notification to inform the user that a form's input can't contain only whitespace." + }, + "inputEmail": { + "message": "Input is not an email address." + }, + "fieldsNeedAttention": { + "message": "$COUNT$ field(s) above need your attention.", + "placeholders": { + "count": { + "content": "$1", + "example": "4" + } + } + }, + "selectPlaceholder": { + "message": "-- Select --" + }, + "multiSelectPlaceholder": { + "message": "-- Type to filter --" + }, + "multiSelectLoading": { + "message": "Retrieving options..." + }, + "multiSelectNotFound": { + "message": "No items found" + }, + "multiSelectClearAll": { + "message": "Clear all" + }, + "plusNMore": { + "message": "+ $QUANTITY$ more", + "placeholders": { + "quantity": { + "content": "$1", + "example": "5" + } + } + }, + "submenu": { + "message": "Submenu" + } +} diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 79b8ae6ab25..5d75ee2208d 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrētas krātuves datņu pielikumiem." }, - "premiumSignUpTwoStep": { - "message": "Tādas papildu divpakāpju pieteikšanās iespējas kā YubiKey, FIDO U2F un Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Paroļu higiēnas, konta veselības un datu noplūžu pārskati, lai uzturētu glabātavu drošu." diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index e7c60b94ba7..20336258846 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrovanog skladišta za priloge datoteka." }, - "premiumSignUpTwoStep": { - "message": "Dodatne opcije prijave u dva koraka kao što su YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Higijena lozinke, zdravlje računa i podaci o krađi podataka kako bi trezor bio siguran." diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index f3f435e22aa..18b448b1d1e 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "ഫയൽ അറ്റാച്ചുമെന്റുകൾക്കായി 1 GB എൻക്രിപ്റ്റുചെയ്‌ത സ്റ്റോറേജ്." }, - "premiumSignUpTwoStep": { - "message": "രണ്ട്-ഘട്ട പ്രവേശന ഓപ്ഷനുകളായ Yubikey, FIDO U2F, Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "നിങ്ങളുടെ വാൾട് സൂക്ഷിക്കുന്നതിന്. പാസ്‌വേഡ് ശുചിത്വം, അക്കൗണ്ട് ആരോഗ്യം, ഡാറ്റ ലംഘന റിപ്പോർട്ടുകൾ." diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index d0277bff90d..29d7954de21 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 900f615b204..f4f65b9f536 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB med kryptert fillagring." }, - "premiumSignUpTwoStep": { - "message": "Ytterligere 2-trinnsinnloggingsmuligheter, slik som YubiKey, FIDO U2F, og Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Passordhygiene, kontohelse, og databruddsrapporter som holder hvelvet ditt trygt." diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 088b2ca8281..a124cede5cf 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB versleutelde opslag voor bijlagen." }, - "premiumSignUpTwoStep": { - "message": "Extra opties voor tweestapsaanmelding zoals YubiKey, FIDO U2F en Duo." + "premiumSignUpTwoStepOptions": { + "message": "Eigen opties voor tweestapsaanmelding zoals YubiKey en Duo." }, "premiumSignUpReports": { "message": "Rapportage op wachtwoordhygiëne, gezondheid van je account en gegevensinbreuk om je kluis veilig te houden." diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index a936f9047e9..46dee838abf 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index cec32d87b39..d6cf45a696e 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 63d724e0b78..ce85ae771dd 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB miejsca na zaszyfrowane załączniki." }, - "premiumSignUpTwoStep": { - "message": "Dodatkowe opcje logowania dwustopniowego, takie jak klucze YubiKey, FIDO U2F oraz Duo." + "premiumSignUpTwoStepOptions": { + "message": "Własnościowe opcje logowania dwuetapowego, takie jak YubiKey i Duo." }, "premiumSignUpReports": { "message": "Raporty bezpieczeństwa haseł, stanu konta i raporty wycieków danych, aby Twoje dane były bezpieczne." diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 781605752cc..8e3aac885c3 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB de armazenamento de arquivos encriptados." }, - "premiumSignUpTwoStep": { - "message": "Opções de autenticação em duas etapas adicionais como YubiKey, FIDO U2F, e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Higiene de senha, saúde da conta, e relatórios sobre violação de dados para manter o seu cofre seguro." diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 5a52982f763..52188383594 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, - "premiumSignUpTwoStep": { - "message": "Opções adicionais de verificação de dois passos, como YubiKey, FIDO U2F e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Higiene de palavras-passe, saúde da conta e relatórios de violação de dados para manter o seu cofre seguro." diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 5e9acf893ea..ca46af9d5d5 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB spațiu de stocare criptat pentru atașamente de fișiere." }, - "premiumSignUpTwoStep": { - "message": "Opțiuni adiționale de autentificare în două etape, cum ar fi YubiKey, FIDO U2F și Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Rapoarte privind igiena parolelor, sănătatea contului și breșele de date pentru a vă păstra seiful în siguranță." diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 61944e41ecd..7785e84f1cf 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 ГБ зашифрованного хранилища для вложенных файлов." }, - "premiumSignUpTwoStep": { - "message": "Дополнительные варианты двухэтапной аутентификации, такие как YubiKey, FIDO U2F и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Гигиена паролей, здоровье аккаунта и отчеты об утечках данных для обеспечения безопасности вашего хранилища." diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 6ed26ccdc6f..e2e5347aeb9 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index af7a73301d8..41411606590 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrovaného úložiska." }, - "premiumSignUpTwoStep": { - "message": "Ďalšie možnosti dvojstupňového prihlásenia ako YubiKey, FIDO U2F a Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Správy o sile hesla, zabezpečení účtov a únikoch dát ktoré vám pomôžu udržať vaše kontá v bezpečí." diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 3da6f653bb5..089da060600 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 246757c9f54..b5712de8625 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1ГБ шифровано складиште за прилоге." }, - "premiumSignUpTwoStep": { - "message": "Додатне опције пријаве у два корака као што су YubiKey, FIDO U2F, и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Извештаји о хигијени лозинки, здравственом стању налога и кршењу података да бисте заштитили сеф." @@ -1493,7 +1493,7 @@ "message": "Одјављени сеф захтева да поново потврдите идентитет да бисте му поново приступили." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Подесите метод откључавања да бисте променили радњу временског ограничења сефа." }, "lock": { "message": "Закључај", @@ -2110,7 +2110,7 @@ "message": "Пријавите се са другим уређајем" }, "loginInitiated": { - "message": "Login initiated" + "message": "Пријава је покренута" }, "notificationSentDevice": { "message": "Обавештење је послато на ваш уређај." @@ -2250,28 +2250,28 @@ "message": "Препоручено ажурирање поставки" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Потребно је одобрење уређаја. Изаберите опцију одобрења испод:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Запамти овај уређај" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Искључите ако се користи јавни уређај" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Одобри са мојим другим уређајем" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Затражити одобрење администратора" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Одобрити са главном лозинком" }, "region": { - "message": "Region" + "message": "Регион" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Потребан је SSO идентификатор организације." }, "eu": { "message": "EU", @@ -2293,40 +2293,40 @@ "message": "Одбијен приступ. Немате дозволу да видите ову страницу." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Налог је успешно креиран!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Захтевано је одобрење администратора" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Ваш захтев је послат вашем администратору." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Бићете обавештени када буде одобрено." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Имате проблема са пријављивањем?" }, "loginApproved": { - "message": "Login approved" + "message": "Пријава је одобрена" }, "userEmailMissing": { - "message": "User email missing" + "message": "Недостаје имејл корисника" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Уређај поуздан" }, "inputRequired": { - "message": "Input is required." + "message": "Унос је потребан." }, "required": { - "message": "required" + "message": "обавезно" }, "search": { - "message": "Search" + "message": "Тражи" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Унос трба имати најмање $COUNT$ слова.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Унос не сме бити већи од $COUNT$ карактера.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Следећи знакови нису дозвољени: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Вредност мора бити најмање $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Вредност не сме бити већа од $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 или више имејлова су неважећи" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Унос не сме да садржи само размак.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Унос није имејл." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ поље(а) изнад захтевај(у) вашу пажњу.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Одабрати --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Тип за филтрирање --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Преузимање опција..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Нема предмета" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Обриши све" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ још $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "Под-мени" } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 3101b8a7228..b224bb083c5 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB krypterad lagring." }, - "premiumSignUpTwoStep": { - "message": "Ytterligare alternativ för tvåstegsverifiering såsom YubiKey, FIDO U2F och Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Lösenordshygien, kontohälsa och dataintrångsrapporter för att skydda ditt valv." diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index d2405f785d9..38e81a83bfd 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index b2210cd5363..32f10e27404 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB of encrypted file storage." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 7919a0f90f8..ff5185c7141 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "Dosya ekleri için 1 GB şifrelenmiş depolama." }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F ve Duo gibi iki aşamalı giriş seçenekleri." + "premiumSignUpTwoStepOptions": { + "message": "YubiKey ve Duo gibi marka bazlı iki aşamalı giriş seçenekleri." }, "premiumSignUpReports": { "message": "Kasanızı güvende tutmak için parola hijyeni, hesap sağlığı ve veri ihlali raporları." diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index afe6b9a8ffd..09a7ec512bc 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 ГБ зашифрованого сховища для файлів." }, - "premiumSignUpTwoStep": { - "message": "Додаткові можливості двоетапної перевірки, наприклад, YubiKey, FIDO U2F та Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Гігієна паролів, здоров'я облікового запису, а також звіти про вразливості даних, щоб зберігати ваше сховище в безпеці." @@ -2317,16 +2317,16 @@ "message": "Довірений пристрій" }, "inputRequired": { - "message": "Input is required." + "message": "Необхідно ввести дані." }, "required": { - "message": "required" + "message": "обов'язково" }, "search": { - "message": "Search" + "message": "Пошук" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Введені дані мають бути довжиною принаймні $COUNT$ символів.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Вхідне значення не повинно перевищувати $COUNT$ символів.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Вказані символи заборонені: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Значення має бути принаймні $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Значення не може перевищувати $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 або більше адрес е-пошти недійсні" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Введене значення не повинно містити лише пробіл.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Введені дані не є адресою е-пошти." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ поле (поля) вище потребують вашої уваги.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Оберіть--" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Введіть для фільтрування --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Параметри отримання..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Нічого не знайдено" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Очистити все" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ ще $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "Підменю" } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 2cee99f327c..dcbedbe2938 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1GB bộ nhớ lưu trữ tập tin được mã hóa." }, - "premiumSignUpTwoStep": { - "message": "Các tùy chọn xác thực hai lớp bổ sung như YubiKey, FIDO U2F và Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "Thanh lọc mật khẩu, kiểm tra an toàn tài khoản và các báo cáo rò rĩ dữ liệu là để giữ cho kho của bạn an toàn." diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 135104c0fc7..ff74b521eed 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "1 GB 文件附件加密存储。" }, - "premiumSignUpTwoStep": { - "message": "额外的两步登录选项,如 YubiKey、FIDO U2F 和 Duo。" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "密码健康、账户体检以及数据泄露报告,保障您的密码库安全。" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index be38a8605fe..1de910b1b44 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -1077,8 +1077,8 @@ "premiumSignUpStorage": { "message": "用於檔案附件的 1 GB 的加密檔案儲存空間。" }, - "premiumSignUpTwoStep": { - "message": "YubiKey、FIDO U2F 和 Duo 等額外的兩步驟登入選項。" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpReports": { "message": "密碼健康度檢查、提供帳戶體檢以及資料外洩報告,以保障您的密碼庫安全。" @@ -1414,7 +1414,7 @@ "message": "啟動時詢問 Touch ID" }, "requirePasswordOnStart": { - "message": "Require password or PIN on app start" + "message": "需要在啟動應用程式時輸入密碼或 PIN 碼。" }, "recommendedForSecurity": { "message": "Recommended for security." @@ -2110,7 +2110,7 @@ "message": "使用其他裝置登入" }, "loginInitiated": { - "message": "Login initiated" + "message": "登入已發起" }, "notificationSentDevice": { "message": "已傳送通知至您的裝置。" @@ -2181,7 +2181,7 @@ "message": "You denied a login attempt from another device. If this really was you, try to log in with the device again." }, "justNow": { - "message": "Just now" + "message": "剛剛" }, "requestedXMinutesAgo": { "message": "Requested $MINUTES$ minutes ago", @@ -2193,7 +2193,7 @@ } }, "loginRequestHasAlreadyExpired": { - "message": "Login request has already expired." + "message": "登入要求已逾期。" }, "thisRequestIsNoLongerValid": { "message": "This request is no longer valid." @@ -2202,7 +2202,7 @@ "message": "Use this device to approve login requests made from other devices." }, "confirmLoginAtemptForMail": { - "message": "Confirm login attempt for $EMAIL$", + "message": "確認 $EMAIL$ 的登入嘗試", "placeholders": { "email": { "content": "$1", @@ -2214,13 +2214,13 @@ "message": "已要求登入" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "已暴露的主密碼" }, "exposedMasterPasswordDesc": { "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "強度不足且已暴露的主密碼" }, "weakAndBreachedMasterPasswordDesc": { "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" @@ -2253,7 +2253,7 @@ "message": "Device approval required. Select an approval option below:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "記住這個裝置" }, "uncheckIfPublicDevice": { "message": "Uncheck if using a public device" @@ -2268,7 +2268,7 @@ "message": "Approve with master password" }, "region": { - "message": "Region" + "message": "區域" }, "ssoIdentifierRequired": { "message": "Organization SSO identifier is required." @@ -2293,10 +2293,10 @@ "message": "拒絕存取。您沒有檢視此頁面的權限。" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "成功建立帳號!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "需要管理員批准" }, "adminApprovalRequestSentToAdmins": { "message": "Your request has been sent to your admin." @@ -2305,7 +2305,7 @@ "message": "You will be notified once approved." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "登入時遇到困難?" }, "loginApproved": { "message": "Login approved" From 326b24e6557e0584110d2be75e4b1eaf5c9deb7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:50:51 +0000 Subject: [PATCH 084/135] Autosync the updated translations (#6166) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 7 +- apps/web/src/locales/ar/messages.json | 7 +- apps/web/src/locales/az/messages.json | 7 +- apps/web/src/locales/be/messages.json | 7 +- apps/web/src/locales/bg/messages.json | 13 ++-- apps/web/src/locales/bn/messages.json | 7 +- apps/web/src/locales/bs/messages.json | 7 +- apps/web/src/locales/ca/messages.json | 7 +- apps/web/src/locales/cs/messages.json | 7 +- apps/web/src/locales/cy/messages.json | 7 +- apps/web/src/locales/da/messages.json | 7 +- apps/web/src/locales/de/messages.json | 7 +- apps/web/src/locales/el/messages.json | 7 +- apps/web/src/locales/en_GB/messages.json | 7 +- apps/web/src/locales/en_IN/messages.json | 7 +- apps/web/src/locales/eo/messages.json | 7 +- apps/web/src/locales/es/messages.json | 7 +- apps/web/src/locales/et/messages.json | 7 +- apps/web/src/locales/eu/messages.json | 7 +- apps/web/src/locales/fa/messages.json | 7 +- apps/web/src/locales/fi/messages.json | 7 +- apps/web/src/locales/fil/messages.json | 7 +- apps/web/src/locales/fr/messages.json | 7 +- apps/web/src/locales/gl/messages.json | 7 +- apps/web/src/locales/he/messages.json | 7 +- apps/web/src/locales/hi/messages.json | 7 +- apps/web/src/locales/hr/messages.json | 7 +- apps/web/src/locales/hu/messages.json | 7 +- apps/web/src/locales/id/messages.json | 7 +- apps/web/src/locales/it/messages.json | 9 ++- apps/web/src/locales/ja/messages.json | 7 +- apps/web/src/locales/ka/messages.json | 7 +- apps/web/src/locales/km/messages.json | 7 +- apps/web/src/locales/kn/messages.json | 7 +- apps/web/src/locales/ko/messages.json | 7 +- apps/web/src/locales/lv/messages.json | 7 +- apps/web/src/locales/ml/messages.json | 7 +- apps/web/src/locales/mr/messages.json | 7 +- apps/web/src/locales/my/messages.json | 7 +- apps/web/src/locales/nb/messages.json | 7 +- apps/web/src/locales/ne/messages.json | 7 +- apps/web/src/locales/nl/messages.json | 7 +- apps/web/src/locales/nn/messages.json | 7 +- apps/web/src/locales/or/messages.json | 7 +- apps/web/src/locales/pl/messages.json | 7 +- apps/web/src/locales/pt_BR/messages.json | 7 +- apps/web/src/locales/pt_PT/messages.json | 7 +- apps/web/src/locales/ro/messages.json | 7 +- apps/web/src/locales/ru/messages.json | 7 +- apps/web/src/locales/si/messages.json | 7 +- apps/web/src/locales/sk/messages.json | 7 +- apps/web/src/locales/sl/messages.json | 7 +- apps/web/src/locales/sr/messages.json | 89 ++++++++++++------------ apps/web/src/locales/sr_CS/messages.json | 7 +- apps/web/src/locales/sv/messages.json | 7 +- apps/web/src/locales/te/messages.json | 7 +- apps/web/src/locales/th/messages.json | 7 +- apps/web/src/locales/tr/messages.json | 7 +- apps/web/src/locales/uk/messages.json | 7 +- apps/web/src/locales/vi/messages.json | 7 +- apps/web/src/locales/zh_CN/messages.json | 13 ++-- apps/web/src/locales/zh_TW/messages.json | 7 +- 62 files changed, 358 insertions(+), 172 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 7d68eb6f4c6..4b80c3c2119 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GG geënkripteerde berging vir lêeraanhegsels." }, - "premiumSignUpTwoStep": { - "message": "Bykomende tweestapaantekenopsies soos YubiKey, FIDO U2F en Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Noodtoegang" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index c5b8fb72e39..f43798b93c1 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "الوصول الطارئ" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index a51fb880f62..6e037753488 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "Fayl qoşmaları üçün 1 GB şifrələnmiş saxlama sahəsi." }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F və Duo kimi iki mərhələli giriş seçimləri." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Fövqəladə vəziyyət müraciəti" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Ödənilməmiş abunəliklər üçün ödəniş üsulunuzdan ödəniş alınacaq." + }, "paymentChargedWithTrial": { "message": "Planınızda 7 günlük ödənişsiz sınaq var. Sınaq müddəti bitənə qədər ödəniş metodundan pul çıxılmayacaq. Faktura, hər $INTERVAL$ bir müntəzəm olaraq icra ediləcək. İstənilən vaxt imtina edə bilərsiniz." }, diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 798ed6258c2..305dcf75092 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 ГБ зашыфраванага сховішча для далучаных файлаў." }, - "premiumSignUpTwoStep": { - "message": "Дадатковыя варыянты двухэтапнага ўваходу, такія як YubiKey, FIDO U2F і Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Экстранны доступ" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "У ваш тарыфны план уключаны выпрабавальны перыяд на 7 дзён. У вас не будзе спагнана плата згодна з выбраным спосабам аплаты пакуль не завяршыцца выпрабавальны перыяд. Вы можаце скасаваць яго ў любы момант." }, diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index c3530f9b835..9b32d828818 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB пространство за файлове, които се шифрират." }, - "premiumSignUpTwoStep": { - "message": "Двустепенно удостоверяване чрез YubiKey, FIDO U2F и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Авариен достъп" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Сумата за всички неплатени абонаменти ще бъде изискана от Вашия метод за плащане." + }, "paymentChargedWithTrial": { "message": "Планът ви идва с пробен период от 7 дена. Плащането няма се изиска преди това, след което ще се повтаря всеки $INTERVAL$. Може да се откажете по всяко време." }, @@ -5170,10 +5173,10 @@ "message": "Премахване на спонсорирането" }, "removeSponsorshipConfirmation": { - "message": "After removing a sponsorship, you will be responsible for this subscription and related invoices. Are you sure you want to continue?" + "message": "След като премахнете спонсорството, Вие ще поемете отговорността за този абонамент и свързаните с него плащания. Наистина ли искате да продължите?" }, "sponsorshipCreated": { - "message": "Sponsorship created" + "message": "Спонсорството е създадено" }, "emailSent": { "message": "Писмото е изпратено" @@ -5290,7 +5293,7 @@ "message": "Migrated to Key Connector" }, "paymentSponsored": { - "message": "Please provide a payment method to associate with the organization. Don't worry, we won't charge you anything unless you select additional features or your sponsorship expires. " + "message": "Посочете метод за плащане, който да бъде свързан с организацията. Не се притеснявайте – от него няма да бъдат изискани никакви суми, докато не изберете допълнителни функционалности или докато не изтече периода на спонсорирането Ви. " }, "orgCreatedSponsorshipInvalid": { "message": "The sponsorship offer has expired. You may delete the organization you created to avoid a charge at the end of your 7 day trial. Otherwise you may close this prompt to keep the organization and assume billing responsibility." diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 07f6478c2f6..c970aacffb7 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "ফাইল সংযুক্তির জন্য ১ জিবি এনক্রিপ্টেড স্থান।" }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F, ও Duo এর মতো অতিরিক্ত দ্বি-পদক্ষেপ লগইন বিকল্পগুলি।" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index b1810d3616a..ea7b495a715 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index bae96a1ffc2..498f1a56615 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, - "premiumSignUpTwoStep": { - "message": "Opcions addicionals d'inici de sessió en dues passes com ara YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Accés d’emergència" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "El vostre pla inclou una prova gratuïta de 7 dies. El mètode de pagament no es cobrarà fins que no s'haja acabat la prova. Podeu cancel·lar-ho en qualsevol moment." }, diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 8f06a3acb29..c18a9fa2044 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrovaného úložiště pro přílohy." }, - "premiumSignUpTwoStep": { - "message": "Další možnosti dvoufázového přihlášení, jako je například YubiKey, FIDO U2F a Duo." + "premiumSignUpTwoStepOptions": { + "message": "Volby proprietálních dvoufázových přihlášení jako je YubiKey a Duo." }, "premiumSignUpEmergency": { "message": "Nouzový přístup" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Za nezaplacené předplatné bude naúčtována částka z Vaší platební metody." + }, "paymentChargedWithTrial": { "message": "Vybraný plán obsahuje bezplatnou 7denní zkušební dobu. Částka z Vašeho účtu nebude stržena, dokud tato zkušební doba neuplyne. Předplatné můžete kdykoli zrušit." }, diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 0124b315f95..3ed144bcaf5 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB krypteret lager til vedhæftede filer." }, - "premiumSignUpTwoStep": { - "message": "Yderligere totrins login-muligheder, såsom YubiKey, FIDO U2F og Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietære totrins-login muligheder, såsom YubiKey og Duo." }, "premiumSignUpEmergency": { "message": "Nødadgang" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Der vil ske opkrævning for evt. ubetalte abonnementer via betalingsmetoden." + }, "paymentChargedWithTrial": { "message": "Dit abonnement indeholder en gratis 7-dages prøveperiode. Din betalingsmetode vil ikke blive debiteret, før prøveperioden er slut. Du kan til enhver tid annullere." }, diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 615f5397c95..07f5f1d5e7a 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB verschlüsselter Speicherplatz für Dateianhänge." }, - "premiumSignUpTwoStep": { - "message": "Zusätzliche Zwei-Faktor-Authentifizierungsmöglichkeiten wie z.B. YubiKey, FIDO U2F und Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Notfallzugriff" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Deine Zahlungsmethode wird für alle unbezahlten Abonnements belastet." + }, "paymentChargedWithTrial": { "message": "Dein Tarif beinhaltet eine kostenlose 7-Tage-Testversion. Deine Zahlungsart wird nicht belastet, bis die Testphase abgelaufen ist. Du erhältst pro $INTERVAL$ eine Rechnung. Eine Kündigung ist zu jeder Zeit möglich." }, diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 834f96c5069..5103325a343 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB κρυπτογραφημένο αποθηκευτικό χώρο για συνημμένα αρχεία." }, - "premiumSignUpTwoStep": { - "message": "Πρόσθετες επιλογές σύνδεσης δύο παραγόντων, όπως το YubiKey, το FIDO U2F και το Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Πρόσβαση Έκτακτης Ανάγκης" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Το πακέτο σας έρχεται με δωρεάν δοκιμή 7 ημερών. Ο τρόπος πληρωμής σας δεν θα χρεωθεί μέχρι να τελειώσει η δοκιμή. Η χρέωση θα πραγματοποιείται σε επαναλαμβανόμενη βάση κάθε $INTERVAL$. Μπορείτε να το ακυρώσετε οποιαδήποτε στιγμή." }, diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index c772bd2c33b..f342a7e81f3 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 4cfdf991cc0..5f31a22c729 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency Access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. Billing will occur on a recurring basis each $INTERVAL$. You may cancel at any time." }, diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index e4ab167381f..8034131dcc5 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB ĉifrita stokado por dosieraj aldonaĵoj." }, - "premiumSignUpTwoStep": { - "message": "Pliaj du-paŝaj ensalutaj opcioj kiel YubiKey, FIDO U2F kaj Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Kriza Aliro" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Via plano venas kun senpaga 7-taga provado. Via pagmaniero ne estos ŝargita ĝis la proceso finiĝos. Fakturado okazos ĉiufoje $INTERVAL$. Vi rajtas nuligi iam ajn." }, diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 7b59b2e8cd4..50e743cb655 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB de almacenamiento de archivos cifrados." }, - "premiumSignUpTwoStep": { - "message": "Opciones adicionales de inicio de sesión de dos pasos como YubiKey, Fido U2F y Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Acceso de emergencia" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your card will not be charged until the trial has ended and on a recurring basis each $INTERVAL$. You may cancel at any time." }, diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 104b001178c..f31e0832366 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB ulatuses krüpteeritud salvestusruum." }, - "premiumSignUpTwoStep": { - "message": "Lisavõimalused kaheastmeliseks kinnitamiseks, näiteks YubiKey, FIDO U2F ja Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Hädaolukorra ligipääs" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Valitud pakett sisaldab 7 päevast prooviperioodi. Krediitkaardilt ei võeta raha enne, kui prooviperiood läbi saab. Väljatoodud summa debiteeritakse iga $INTERVAL$. Tellimust on võimalik igal ajal tühistada." }, diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index e4f714fe225..793f4612ca2 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "Eranskinentzako 1GB-eko zifratutako biltegia." }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F eta Duo bezalako bi urratseko saio hasierarako aukera gehigarriak." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Larrialdietarako sarbidea" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Zure planak 7 eguneko doako probaldia du. Zure ordainketa ez da kobratuko probaldia amaitu arte. Edozein unetan utz dezakezu bertan behera." }, diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 73f4c95644c..fe48e771fa0 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "۱ گیگابایت فضای ذخیره‌سازی رمزنگاری شده برای پرونده‌های پیوست." }, - "premiumSignUpTwoStep": { - "message": "گزینه‌های ورود دو مرحله‌ای اضافی مانند YubiKey, FIDO U2F و Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "دسترسی اضطراری" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "طرح شما با یک دوره آزمایشی رایگان ۷ روزه همراه است. تا پایان دوره آزمایشی از روش پرداخت شما هزینه ای کسر نمی‌شود. هر زمان که مایل بودید می‌توانید آن را لغو کنید." }, diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 016f1ee256d..1606ca35ff6 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, - "premiumSignUpTwoStep": { - "message": "Muita kaksivaiheisen kirjautumisen todentajia, kuten YubiKey, FIDO U2F ja Duo Security." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Varmuuskäyttö" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Maksutavaltasi veloitetaan kaikki maksamattomat tilauksesta." + }, "paymentChargedWithTrial": { "message": "Tilauksesi sisältää ilmaisen 7 päivän kokeilujakson. Maksutapaasi ei veloiteta ennen kokeilujakson päättymistä. Voit irtisanoa tilauksen koska tahansa." }, diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 8cc5a44b3f1..1b11567b782 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1GB na naka-encrypt na storage para sa mga file attachment." }, - "premiumSignUpTwoStep": { - "message": "Karagdagang mga opsyon para sa dalawang-hakbang na pag-log in tulad ng YubiKey, FIDO U2F, at Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "May libreng 7 araw na trial ang plano mo. Hindi sisingilin ang paraan mo sa pagbabayad hanggang sa matapos ang libreng trial. Makakapagkansela ka kailanman." }, diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 62da8e84e12..6bc5b0a1454 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 Go de stockage chiffré pour les fichiers joints." }, - "premiumSignUpTwoStep": { - "message": "Options additionnelles d'authentification à deux facteurs telles que YubiKey, FIDO U2F et Duo." + "premiumSignUpTwoStepOptions": { + "message": "Options de connexion propriétaires à deux facteurs telles que YubiKey et Duo." }, "premiumSignUpEmergency": { "message": "Accès d'urgence" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Votre mode de paiement sera facturé pour tous les abonnements impayés." + }, "paymentChargedWithTrial": { "message": "Votre offre comprend un essai gratuit de 7 jours. Votre mode de paiement ne sera pas facturé avant la fin de la période d'essai. Vous pouvez annuler à tout moment." }, diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index dbb202a515e..716f2d46071 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 ג'יגה של מקום אחסון מוצפן עבור קבצים מצורפים." }, - "premiumSignUpTwoStep": { - "message": "אפשרויות כניסה דו שלבית מתקדמות כמו YubiKey, FIDO U2F, וגם Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "התוכנית שבחרת מגיעה עם 7 ימי נסיון חינמי. שיטת התשלום שבחרת לא תחויב עד לתום תקופת הנסיון. ביצוע החשבון יתבצע על בסיס מתחדש בכל $INTERVAL$. באפשרותך לבטל בכל עת." }, diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 53f6e802ee9..cb737f84f44 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index d1682ac5301..3d8d176514c 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB šifriranog prostora za pohranu podataka." }, - "premiumSignUpTwoStep": { - "message": "Dodatne mogućnosti za prijavu dvostrukom autentifikacijom kao što su YubiKey, FIDO U2F i Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Pristup u nuždi" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Plan dolazi s besplatnom probnom verzijom od 7 dana. Tvoj način plaćanja neće biti terećen dok ne završi probno razdoblje. Možeš otkazati u bilo kojem trenutku." }, diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index ddd4f5872bb..c8c8439626f 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB titkosított tárhely a fájlmellékleteknek." }, - "premiumSignUpTwoStep": { - "message": "További olyan kétlépcsős bejelentkezési opciók mint a YubiKey, FIDO U2F és Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Sürgősségi hozzáférés" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "A fizetési mód minden ki nem fizetett előfizetésért megterhelésre kerül." + }, "paymentChargedWithTrial": { "message": "A jelenlegi csomag 7 napos ingyenes próbaidőszakot tartalmaz. A fizetési módot az időszak végéig nem terheljük. A csomag bármikor lemondható." }, diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 344f13e2df0..a88799f1adc 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "Penyimpanan terenkripsi 1 GB untuk lampiran file." }, - "premiumSignUpTwoStep": { - "message": "Opsi login dua langkah tambahan seperti YubiKey, FIDO U2F, dan Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Akses darurat" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Paket Anda dilengkapi dengan uji coba gratis selama 7 hari. Metode pembayaran Anda tidak akan ditagih hingga uji coba berakhir. Penagihan akan dilakukan secara berulang setiap $INTERVAL$. Anda dapat membatalkannya kapan saja." }, diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 4900c68ae7c..c082b3dd9ca 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB di spazio di archiviazione criptato per gli allegati." }, - "premiumSignUpTwoStep": { - "message": "Più opzioni di verifica in due passaggi come YubiKey, FIDO U2F, e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Accesso di emergenza" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Eventuali abbonamenti non pagati saranno addebitati sul tuo metodo di pagamento." + }, "paymentChargedWithTrial": { "message": "Il tuo piano include una prova gratis di 7 giorni. Il tuo metodo di pagamento non sarà addebitato fino alla fine del periodo di prova. Puoi cancellarlo in qualsiasi momento." }, @@ -7160,7 +7163,7 @@ "message": "Accesso effettuato!" }, "smBetaEndedDesc": { - "message": "La beta del Gestore dei Segreti è terminata in $BETA_ENDING_DATE$. Ti rimangono $DAYS$ giorni per aggiungere il Gestore dei Segreti al tuo abbonamento a pagamento e mantenere l'accesso ai dati del Gestore dei Segreti. Contatta il Successo del Cliente per aggiungere il Gestore dei Segreti al tuo abbonamento.", + "message": "La beta del Gestore dei Segreti è terminata in $BETA_ENDING_DATE$. Ti rimangono $DAYS$ giorni per aggiungere il Gestore dei Segreti al tuo abbonamento a pagamento e mantenere l'accesso ai dati del Gestore dei Segreti. Contatta l'assistenza per aggiungere il Gestore dei Segreti al tuo abbonamento.", "placeholders": { "beta_ending_date": { "content": "$1", diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 7a6c9c17439..95647694572 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1GB の暗号化されたファイルストレージ" }, - "premiumSignUpTwoStep": { - "message": "YubiKey、FIDO U2F、Duoなどの追加の2段階認証ログインオプション" + "premiumSignUpTwoStepOptions": { + "message": "YubiKey、Duo などのプロプライエタリな2段階認証オプション。" }, "premiumSignUpEmergency": { "message": "緊急アクセス" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "未払いのサブスクリプションについては、指定した支払い方法へ請求されます。" + }, "paymentChargedWithTrial": { "message": "ご利用のプランでは、7日間の無料トライアルが可能です。トライアル期間が終わるまでは課金されません。トライアル終了後、$INTERVAL$毎に請求されます。いつでもキャンセルできます。" }, diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index b0b9f6bd139..5f461f4b70b 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index d22c69e97e2..94145e99c35 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "ಫೈಲ್ ಲಗತ್ತುಗಳಿಗಾಗಿ 1 ಜಿಬಿ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಿದ ಸಂಗ್ರಹ." }, - "premiumSignUpTwoStep": { - "message": "ಹೆಚ್ಚುವರಿ ಎರಡು-ಹಂತದ ಲಾಗಿನ್ ಆಯ್ಕೆಗಳಾದ ಯೂಬಿಕೆ, ಎಫ್‌ಐಡಿಒ ಯು 2 ಎಫ್, ಮತ್ತು ಡ್ಯುವೋ." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "ತುರ್ತು ಪ್ರವೇಶ" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "ನಿಮ್ಮ ಯೋಜನೆ 7 ದಿನಗಳ ಉಚಿತ ಪ್ರಯೋಗದೊಂದಿಗೆ ಬರುತ್ತದೆ. ಪ್ರಯೋಗ ಮುಗಿಯುವವರೆಗೆ ನಿಮ್ಮ ಪಾವತಿ ವಿಧಾನಕ್ಕೆ ಶುಲ್ಕ ವಿಧಿಸಲಾಗುವುದಿಲ್ಲ. ಪ್ರತಿ $INTERVAL$ ಮರುಕಳಿಸುವ ಆಧಾರದ ಮೇಲೆ ಬಿಲ್ಲಿಂಗ್ ಸಂಭವಿಸುತ್ತದೆ. ನೀವು ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ರದ್ದುಗೊಳಿಸಬಹುದು." }, diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index bd8ee6b5db8..9900cfa742e 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1GB의 암호화된 파일 저장소." }, - "premiumSignUpTwoStep": { - "message": "YubiKey나 FIDO U2F, Duo 등의 추가적인 2단계 인증 옵션." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "긴급 접근" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "귀하의 플랜은 7일 무료 평가판입니다. 평가 기간이 만료될 때까지 카드에서 대금이 지불되지 않습니다. 이후 정기적으로 매 $INTERVAL$ 청구됩니다. 언제든지 취소할 수 있습니다." }, diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index d2984de94bc..a1f9a5bc382 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrētas krātuves datņu pielikumiem." }, - "premiumSignUpTwoStep": { - "message": "Tādas papildu divpakāpju pieteikšanās iespējas kā YubiKey, FIDO U2F un Duo." + "premiumSignUpTwoStepOptions": { + "message": "Tādas slēgtā pirmavota divpakāpju pieteikšanās iespējas kā YubiKey un Duo." }, "premiumSignUpEmergency": { "message": "Ārkārtas piekļuve" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Izvēlētais maksājumu veids tiks izmantots jebkuru neapmaksātu abonementu apmaksai." + }, "paymentChargedWithTrial": { "message": "Pašreizējā plānā ir iekļauts bezmaksas 7 dienu izmēģinājuma laiks. Izvēlētais apmaksas veids netiks izmantots līdz izmēģinājuma beigā. Norēķini notiks katru $INTERVAL$. To var atcelt jebkurā brīdī." }, diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 483a78d6491..1623cf8c2f4 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "ഫയൽ അറ്റാച്ചുമെന്റുകൾക്കായി 1 GB എൻക്രിപ്റ്റുചെയ്‌ത സ്റ്റോറേജ്." }, - "premiumSignUpTwoStep": { - "message": "രണ്ട്-ഘട്ട പ്രവേശന ഓപ്ഷനുകളായ Yubikey, FIDO U2F, Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. Billing will occur on a recurring basis each $INTERVAL$. You may cancel at any time." }, diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 4f996710ba8..5d0b7f5eafd 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB med kryptert fillagring." }, - "premiumSignUpTwoStep": { - "message": "Ytterligere 2-trinnsinnloggingsmuligheter, slik som YubiKey, FIDO U2F, og Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Nødtilgang" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Funksjonsplanen din kommer med en gratis 7-dagersprøveperiode. Din betalingsmetode vil ikke bli trekt før prøveperiode har utløpt. Regningstrekk vil skje på en gjentakende basis hver(t) $INTERVAL$. Du kan avbryte når som helst." }, diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index c28f306acb3..4a8c2e06c0c 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 6b12ef13058..dcead7cd8fd 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB versleutelde opslag voor bijlagen." }, - "premiumSignUpTwoStep": { - "message": "Extra tweestapsaanmeldingsopties zoals YubiKey, FIDO U2F en Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Noodtoegang" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "We brengen onbetaalde abonnementen in rekening bij je betalingsmethode." + }, "paymentChargedWithTrial": { "message": "Je lidmaatschap omvat een gratis proefperiode van 7 dagen. Kosten worden pas in rekening gebracht als de proefperiode voorbij is. De betaling vindt ieder(e) $INTERVAL$ op terugkerende basis plaats. Je kunt op ieder moment opzeggen." }, diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 81ccc76c41c..22e4a7d30e1 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 757d09a97e5..80e7dd695d9 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB przestrzeni na zaszyfrowane załączniki." }, - "premiumSignUpTwoStep": { - "message": "Dodatkowe opcje logowania dwustopniowego, takie jak klucze YubiKey, FIDO U2F oraz Duo." + "premiumSignUpTwoStepOptions": { + "message": "Własnościowe opcje logowania dwuetapowego, takie jak YubiKey i Duo." }, "premiumSignUpEmergency": { "message": "Dostęp awaryjny" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Twoja metoda płatności zostanie obciążona opłatą za wszystkie nieopłacone subskrypcje." + }, "paymentChargedWithTrial": { "message": "Twój plan zawiera 7-dniowy okres próbny. W tym czasie nie poniesiesz żadnych kosztów. Możesz zrezygnować z niego w każdej chwili." }, diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 2b9a1844de8..3d931f81d07 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB de armazenamento de arquivos encriptados." }, - "premiumSignUpTwoStep": { - "message": "Opções adicionais de login em duas etapas, como YubiKey, FIDO U2F e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Acesso de Emergência" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Seu método de pagamento será cobrado por qualquer assinatura não paga." + }, "paymentChargedWithTrial": { "message": "Seu plano vem com um teste gratuito de 7 dias. Seu cartão não será cobrado até que o período de teste termine e de forma recorrente a cada $INTERVAL$. Você pode cancelar a qualquer momento." }, diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index fe115a0ff91..0bf7e684824 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, - "premiumSignUpTwoStep": { - "message": "Opções adicionais de verificação de dois passos, como YubiKey, FIDO U2F e Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Acesso de emergência" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "O seu método de pagamento será cobrado por quaisquer subscrições não pagas." + }, "paymentChargedWithTrial": { "message": "O seu plano inclui um teste gratuito de 7 dias. O seu método de pagamento não será cobrado até ao fim do período de avaliação. Pode cancelar a qualquer momento." }, diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 118c3570ecc..27e9d4c17b7 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB stocare criptată pentru fișiere atașate." }, - "premiumSignUpTwoStep": { - "message": "Opțiuni suplimentare de conectare în două etape, cum ar fi YubiKey, FIDO U2F și Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Acces de urgență" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Planul dvs. vine cu o încercare gratuită de 7 zile. Metoda dvs. de plată nu va fi facturată până la sfârșitul perioadei de încercare. Puteți anula în orice moment." }, diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 55b6724ad61..af9a088993f 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 ГБ зашифрованного хранилища для вложенных файлов." }, - "premiumSignUpTwoStep": { - "message": "Дополнительные варианты двухэтапной аутентификации, такие как YubiKey, FIDO U2F и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Проприетарные варианты двухэтапной аутентификации, такие как YubiKey или Duo." }, "premiumSignUpEmergency": { "message": "Экстренный доступ" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "За любые неоплаченные подписки будет взиматься плата с вашего способа оплаты." + }, "paymentChargedWithTrial": { "message": "Ваш план включает семидневную бесплатную пробную версию. Ваш метод оплаты не будет использован до окончания пробной версии. Оплата будет выполняться каждый $INTERVAL$. Вы можете отказаться в любое время." }, diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 855a48deda9..adff63eb2fc 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 0ab0af5a3e6..fd1d6d707bf 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB šifrovaného úložiska pre prílohy." }, - "premiumSignUpTwoStep": { - "message": "Ďalšie možnosti dvojstupňového prihlásenia ako YubiKey, FIDO U2F a Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Núdzový prístup" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Všetky nezaplatené predplatné budú účtované prostredníctvom vášho spôsobu platby." + }, "paymentChargedWithTrial": { "message": "Váš plán ponúka 7-dňovú skúšobnú dobu zadarmo. Z vašej platobnej metódy nebude stiahnutý poplatok, kým sa neskončí skúšobná doba. Plán môžete kedykoľvek zrušiť." }, diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 1cb26cd5d8e..c3f3e479f1f 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Dostop v sili" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index 3683cf2b20c..f1ae3e829c1 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -955,7 +955,7 @@ "message": "Копирај верификациони код" }, "copyUuid": { - "message": "Copy UUID" + "message": "Копирај UUID" }, "warning": { "message": "Упозорење" @@ -1365,7 +1365,7 @@ "message": "Прикажи иконе сајтова" }, "faviconDesc": { - "message": "Прикажи препознатљиву слику поред сваке ставке за пријаву." + "message": "Прикажи препознатљиву иконицу поред сваке ставке за пријаву." }, "enableFullWidth": { "message": "Упали пуни ширину распореда", @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1ГБ шифровано складиште за прилоге." }, - "premiumSignUpTwoStep": { - "message": "Додатне опције пријаве у два корака као што су YubiKey, FIDO U2F, и Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Улаз у хитним случајевима" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Ваш план долази са бесплатним 7-дневним пробним периодом. Начин плаћања неће бити наплаћен док се пробно време не заврши. Наплата ће се вршити периодично, сваки $INTERVAL$. Можете отказати било када." }, @@ -3537,7 +3540,7 @@ "message": "Изаберите када ће сеф истећи и да изврши одабрану радњу." }, "vaultTimeoutLogoutDesc": { - "message": "Choose when your vault will be logged out." + "message": "Одаберите када ће ваш сеф бити одјављен." }, "oneMinute": { "message": "1 минут" @@ -4936,7 +4939,7 @@ "message": "Онемогућите извоз личног сефа" }, "disablePersonalVaultExportDescription": { - "message": "Do not allow members to export data from their individual vault." + "message": "Не дозволи члановима да извозе податке из свог индивидуалног сефа." }, "vaultExportDisabled": { "message": "Извоз сефа онемогућен" @@ -5440,7 +5443,7 @@ "message": "Извоз сефа организације" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Само појединачне ставке сефа повезане са $EMAIL$ ће бити извењене. Ставке организационог сефа неће бити укључене. Само информације о ставкама из сефа ће бити извезене и неће укључивати повезане прилоге.", "placeholders": { "email": { "content": "$1", @@ -6839,58 +6842,58 @@ "message": "Ажурирати KDF подешавања" }, "loginInitiated": { - "message": "Login initiated" + "message": "Пријава је покренута" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Потребно је одобрење уређаја. Изаберите опцију одобрења испод:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Запамти овај уређај" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Искључите ако се користи јавни уређај" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Одобри са мојим другим уређајем" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Затражити одобрење администратора" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Одобрити са главном лозинком" }, "trustedDeviceEncryption": { - "message": "Trusted device encryption" + "message": "Шифровање поузданог уређаја" }, "trustedDevices": { "message": "Поуздани уређаји" }, "memberDecryptionOptionTdeDescriptionPartOne": { - "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", + "message": "Када се аутентификују, чланови ће дешифровати податке из сефљ користећи кључ сачуван на њиховом уређају", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkOne": { - "message": "single organization", + "message": "јединствена организација", "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.'" }, "memberDecryptionOptionTdeDescriptionPartTwo": { - "message": "policy,", + "message": "полиса,", "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": { - "message": "policy, and", + "message": "полиса, и", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkThree": { - "message": "account recovery administration", + "message": "администрација опоравка налога", "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.'" }, "memberDecryptionOptionTdeDescriptionPartFour": { - "message": "policy with automatic enrollment will turn on when this option is used.", + "message": "полисе са аутоматским уписом ће се укључити када се користи ова опција.", "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.'" }, "notFound": { @@ -6981,7 +6984,7 @@ "message": "Уклањање чланова који немају главну лозинку без постављања једне за њих може ограничити приступ њиховом пуном налогу." }, "approvedAuthRequest": { - "message": "Approved device for $ID$.", + "message": "Одобрен уређај за $ID$.", "placeholders": { "id": { "content": "$1", @@ -6990,7 +6993,7 @@ } }, "rejectedAuthRequest": { - "message": "Denied device for $ID$.", + "message": "Одбијен уређај за $ID$.", "placeholders": { "id": { "content": "$1", @@ -6999,7 +7002,7 @@ } }, "requestedDeviceApproval": { - "message": "Requested device approval." + "message": "Затражено је одобрење уређаја." }, "startYour7DayFreeTrialOfBitwardenFor": { "message": "Започните своју 7-дневну бесплатну пробну Bitwarden-а за $ORG$", @@ -7023,28 +7026,28 @@ "message": "Одабрана застава" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Налог је успешно креиран!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Захтевано је одобрење администратора" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Ваш захтев је послат вашем администратору." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Бићете обавештени када буде одобрено." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Имате проблема са пријављивањем?" }, "loginApproved": { - "message": "Login approved" + "message": "Пријава је одобрена" }, "userEmailMissing": { - "message": "User email missing" + "message": "Недостаје имејл корисника" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Уређај поуздан" }, "sendsNoItemsTitle": { "message": "Нема активних Sends", @@ -7070,13 +7073,13 @@ "message": "For engineering and DevOps teams to manage secrets throughout the software development lifecycle." }, "free2PersonOrganization": { - "message": "Free 2-person Organizations" + "message": "Бесплатна организација за 2 особе" }, "unlimitedSecrets": { - "message": "Unlimited secrets" + "message": "Неограничене тајне" }, "unlimitedProjects": { - "message": "Unlimited projects" + "message": "Неограничени пројекти" }, "projectsIncluded": { "message": "$COUNT$ projects included", @@ -7106,13 +7109,13 @@ } }, "addSecretsManager": { - "message": "Add Secrets Manager" + "message": "Додати Менаџер Тајни" }, "addSecretsManagerUpgradeDesc": { "message": "Add Secrets Manager to your upgraded plan to maintain access to any secrets created with your previous plan." }, "additionalServiceAccounts": { - "message": "Additional service accounts" + "message": "Додатни сервисни налози" }, "includedServiceAccounts": { "message": "Ваш план долази са $COUNT$ налога сервиса.", @@ -7133,16 +7136,16 @@ } }, "passwordManagerPlanPrice": { - "message": "Password Manager plan price" + "message": "Цена плана менаџера лозинки" }, "secretsManagerPlanPrice": { - "message": "Secrets Manager plan price" + "message": "Цена плана менаџера тајни" }, "passwordManager": { "message": "Менаџер лозинки" }, "freeOrganization": { - "message": "Free Organization" + "message": "Бесплатна организација" }, "limitServiceAccounts": { "message": "Limit service accounts (optional)" @@ -7157,7 +7160,7 @@ "message": "Max potential service account cost" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Пријављено!" }, "smBetaEndedDesc": { "message": "Бета менаџера тајни се завршио $BETA_ENDING_DATE$. Остало вам је $DAYS$ дана да додате Менаџер тајни у вашу претплату и да задржите приступ подацима Манагера тајни. Контактирајте подршку да додате Менаџер тајни у своју претплату.", @@ -7179,6 +7182,6 @@ "message": "Бета" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "Већ имате налог?" } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 3daeae10af7..a50306355db 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index df8d315d6d7..7d2e12239f7 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB krypterad lagring." }, - "premiumSignUpTwoStep": { - "message": "Ytterligare alternativ för tvåstegsverifiering såsom YubiKey, FIDO U2F och Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Nödåtkomst" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Din betalningsmetod kommer att debiteras för eventuella obetalda prenumerationer." + }, "paymentChargedWithTrial": { "message": "Din plan kommer med en kostnadsfri 7-dagars provperiod. Din betalningsmetod kommer inte att debiteras förrän provperioden har upphört. Du kan avbryta när som helst." }, diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index e416094e183..a71551e7710 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 7173b6d8d90..25206db2e37 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Your plan comes with a free 7 day trial. Your payment method will not be charged until the trial has ended. You may cancel at any time." }, diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 9111d52fce2..55b2c51548e 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "Dosya ekleri için 1 GB şifrelenmiş depolama." }, - "premiumSignUpTwoStep": { - "message": "YubiKey, FIDO U2F ve Duo gibi iki aşamalı giriş seçenekleri." + "premiumSignUpTwoStepOptions": { + "message": "YubiKey ve Duo gibi marka bazlı iki aşamalı giriş seçenekleri." }, "premiumSignUpEmergency": { "message": "Acil durum erişimi" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Paketiniz 7 günlük ücretsiz deneme süresiyle geliyor. Deneme süresi bitene kadar sizden ücret alınmayacak. İstediğiniz zaman aboneliğinizi iptal edebilirsiniz." }, diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index f3e5519cd76..4c336c0b30d 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 ГБ зашифрованого сховища для файлів." }, - "premiumSignUpTwoStep": { - "message": "Додаткові можливості двоетапної перевірки, наприклад, YubiKey, FIDO U2F та Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Екстрений доступ" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Ваш тарифний план має 7 днів безплатного пробного періоду. З вас не буде стягнуто плату до завершення цього періоду. Ви можете скасувати це в будь-який час." }, diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 2095ec36cb1..fb25525d61a 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, - "premiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "Emergency access" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "Gói của bạn đi kèm với 7 ngày dùng thử miễn phí. Phương thức thanh toán của bạn sẽ không bị tính phí cho đến khi hết thời gian dùng thử. Việc thanh toán sẽ thực hiện định kỳ mỗi $INTERVAL$. Bạn có thể hủy bỏ bất cứ lúc nào." }, diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index f38192eb899..be07ab7e208 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -606,7 +606,7 @@ "message": "登录或者创建一个账户来访问您的安全密码库。" }, "loginWithDevice": { - "message": "设备登录" + "message": "使用设备登录" }, "loginWithDeviceEnabledNote": { "message": "设备登录必须在 Bitwarden 应用程序的设置中设启用。需要其他选项吗?" @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "1 GB 文件附件加密存储。" }, - "premiumSignUpTwoStep": { - "message": "额外的两步登录选项,如 YubiKey、FIDO U2F 和 Duo。" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "紧急访问" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "任何未付费订阅都将通过您的付款方式收取费用。" + }, "paymentChargedWithTrial": { "message": "您的计划包含了 7 天的免费试用期。在试用期结束前,不会从您的付款方式中扣款。您可以随时取消。" }, @@ -2448,7 +2451,7 @@ "message": "组织已创建" }, "organizationReadyToGo": { - "message": "你的组织准备好了!" + "message": "您的新组织已准备就绪!" }, "organizationUpgraded": { "message": "组织已升级" @@ -6936,7 +6939,7 @@ "message": "设备信息" }, "time": { - "message": "Time" + "message": "时间" }, "denyAllRequests": { "message": "拒绝所有请求" diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index c988b11a946..acdd1aa1a79 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -1924,8 +1924,8 @@ "premiumSignUpStorage": { "message": "用於檔案附件的 1 GB 的加密檔案儲存空間。" }, - "premiumSignUpTwoStep": { - "message": "YubiKey、FIDO U2F 和 Duo 等額外的兩步驟登入選項。" + "premiumSignUpTwoStepOptions": { + "message": "Proprietary two-step login options such as YubiKey and Duo." }, "premiumSignUpEmergency": { "message": "緊急存取" @@ -2040,6 +2040,9 @@ } } }, + "paymentChargedWithUnpaidSubscription": { + "message": "Your payment method will be charged for any unpaid subscriptions." + }, "paymentChargedWithTrial": { "message": "您的方案包含了 7 天的免費試用。在試用期結束之前,不會從您的付款方式中扣款。您可以隨時取消。" }, From bf7aa6473ee8e0a2d5e9055c3b4ec21571f306d0 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:18:20 -0700 Subject: [PATCH 085/135] [PM-1509] Accessibility for elements (#5686) * change code color to meet accessibility requirements * updates to desktop and web * adjust colors for desktop, web, and browser * update color values * switch nord color to use same as Tailwind theme * align variable names --- apps/browser/src/popup/scss/variables.scss | 12 ++++++++---- apps/desktop/src/scss/variables.scss | 8 +++++--- apps/web/src/scss/variables.scss | 6 ++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/browser/src/popup/scss/variables.scss b/apps/browser/src/popup/scss/variables.scss index 2cdc49cd9ef..d8891cf620b 100644 --- a/apps/browser/src/popup/scss/variables.scss +++ b/apps/browser/src/popup/scss/variables.scss @@ -43,6 +43,10 @@ $button-color: lighten($text-color, 40%); $button-color-primary: darken($brand-primary, 8%); $button-color-danger: darken($brand-danger, 10%); +$code-color: #c01176; +$code-color-dark: #f08dc7; +$code-color-nord: #dbb1d5; + $solarizedDarkBase03: #002b36; $solarizedDarkBase02: #073642; $solarizedDarkBase01: #586e75; @@ -122,7 +126,7 @@ $themes: ( // light has no hover so use same color webkitCalendarPickerHoverFilter: invert(46%) sepia(69%) saturate(6397%) hue-rotate(211deg) brightness(85%) contrast(103%), - codeColor: #e83e8c, + codeColor: $code-color, ), dark: ( textColor: #ffffff, @@ -184,7 +188,7 @@ $themes: ( hue-rotate(184deg) brightness(87%) contrast(93%), webkitCalendarPickerHoverFilter: brightness(0) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(93deg) brightness(103%) contrast(103%), - codeColor: #e83e8c, + codeColor: $code-color-dark, ), nord: ( textColor: $nord5, @@ -246,7 +250,7 @@ $themes: ( // has no hover so use same color webkitCalendarPickerHoverFilter: brightness(0) saturate(100%) invert(94%) sepia(5%) saturate(454%) hue-rotate(185deg) brightness(93%) contrast(96%), - codeColor: #e83e8c, + codeColor: $code-color-nord, ), solarizedDark: ( textColor: $solarizedDarkBase2, @@ -307,7 +311,7 @@ $themes: ( hue-rotate(138deg) brightness(92%) contrast(90%), webkitCalendarPickerHoverFilter: brightness(0) saturate(100%) invert(94%) sepia(10%) saturate(462%) hue-rotate(345deg) brightness(103%) contrast(87%), - codeColor: #e83e8c, + codeColor: $code-color-dark, ), ); diff --git a/apps/desktop/src/scss/variables.scss b/apps/desktop/src/scss/variables.scss index 3ad4c0f0754..e4a2f124768 100644 --- a/apps/desktop/src/scss/variables.scss +++ b/apps/desktop/src/scss/variables.scss @@ -41,7 +41,9 @@ $button-color: lighten($text-color, 40%); $button-color-primary: darken($brand-primary, 8%); $button-color-danger: darken($brand-danger, 10%); -$code-color: #e83e8c; +$code-color: #c01176; +$code-color-dark: #f08dc7; +$code-color-nord: #dbb1d5; $themes: ( light: ( @@ -158,7 +160,7 @@ $themes: ( accountSwitcherTextColor: #ffffff, svgSuffix: "-dark.svg", hrColor: #bac0ce, - codeColor: $code-color, + codeColor: $code-color-dark, ), nord: ( textColor: $nord5, @@ -216,7 +218,7 @@ $themes: ( accountSwitcherTextColor: $nord5, svgSuffix: "-dark.svg", hrColor: $nord4, - codeColor: $code-color, + codeColor: $code-color-nord, ), ); diff --git a/apps/web/src/scss/variables.scss b/apps/web/src/scss/variables.scss index 719f403e385..af61daff512 100644 --- a/apps/web/src/scss/variables.scss +++ b/apps/web/src/scss/variables.scss @@ -88,6 +88,7 @@ $mfaTypes: 0, 2, 3, 4, 6; $lightDangerHover: #c43421; $lightInputColor: #465057; $lightInputPlaceholderColor: #b6b8b8; +$lightCodeColor: #c01176; // Dark @@ -107,6 +108,7 @@ $darkDarkBlue1: #2f343d; $darkDarkBlue2: #1f242e; $darkInputColor: $white; $darkInputPlaceholderColor: $darkGrey1; +$darkCodeColor: #f08dc7; $themes: ( light: ( @@ -167,7 +169,7 @@ $themes: ( calloutBackground: #fafafa, calloutColor: #212529, cdkDraggingBackground: $white, - codeColor: #e83e8c, + codeColor: $lightCodeColor, dropdownBackground: $white, dropdownHover: rgba(0, 0, 0, 0.06), dropdownTextColor: $body-color, @@ -276,7 +278,7 @@ $themes: ( calloutBackground: $darkBlue2, calloutColor: $white, cdkDraggingBackground: $darkDarkBlue1, - codeColor: #e83e8c, + codeColor: $darkCodeColor, dropdownBackground: $darkDarkBlue1, dropdownHover: rgba(255, 255, 255, 0.03), dropdownTextColor: $white, From a920d62dfeb3f339a366097376c8ef52d2360471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pereira?= Date: Mon, 4 Sep 2023 21:01:16 +0100 Subject: [PATCH 086/135] [PM-3326] [CLI] Add minNumber, minSpecial and ambiguous password generation options (#5974) * feat(cli): add minNumber option and pass to generation service * feat(cli): add minSpecial option and pass to generation service * feat(cli): add ambiguous option and pass to generation service * refactor: extract utils to convert number and string options * feat(ts): add types to utils and options * feat: validate result of parsed value in convertNumberOption util --- apps/cli/src/program.ts | 3 +++ apps/cli/src/tools/generate.command.ts | 18 ++++++++++++++---- apps/cli/src/utils.ts | 16 ++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index 9eca236a3a0..8bca024b410 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -298,9 +298,12 @@ export class Program { .option("-p, --passphrase", "Generate a passphrase.") .option("--length ", "Length of the password.") .option("--words ", "Number of words.") + .option("--minNumber ", "Minimum number of numeric characters.") + .option("--minSpecial ", "Minimum number of special characters.") .option("--separator ", "Word separator.") .option("-c, --capitalize", "Title case passphrase.") .option("--includeNumber", "Passphrase includes number.") + .option("--ambiguous", "Avoid ambiguous characters.") .on("--help", () => { writeLn("\n Notes:"); writeLn(""); diff --git a/apps/cli/src/tools/generate.command.ts b/apps/cli/src/tools/generate.command.ts index bd9ad88a04f..30436e7db71 100644 --- a/apps/cli/src/tools/generate.command.ts +++ b/apps/cli/src/tools/generate.command.ts @@ -1,5 +1,6 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { PasswordGeneratorOptions } from "@bitwarden/common/tools/generator/password/password-generator-options"; import { Response } from "../models/response"; import { StringResponse } from "../models/response/string.response"; @@ -13,7 +14,7 @@ export class GenerateCommand { async run(cmdOptions: Record): Promise { const normalizedOptions = new Options(cmdOptions); - const options = { + const options: PasswordGeneratorOptions = { uppercase: normalizedOptions.uppercase, lowercase: normalizedOptions.lowercase, number: normalizedOptions.number, @@ -24,6 +25,9 @@ export class GenerateCommand { numWords: normalizedOptions.words, capitalize: normalizedOptions.capitalize, includeNumber: normalizedOptions.includeNumber, + minNumber: normalizedOptions.minNumber, + minSpecial: normalizedOptions.minSpecial, + ambiguous: normalizedOptions.ambiguous, }; const enforcedOptions = (await this.stateService.getIsAuthenticated()) @@ -47,6 +51,9 @@ class Options { words: number; capitalize: boolean; includeNumber: boolean; + minNumber: number; + minSpecial: number; + ambiguous: boolean; constructor(passedOptions: Record) { this.uppercase = CliUtils.convertBooleanOption(passedOptions?.uppercase); @@ -55,10 +62,13 @@ class Options { this.special = CliUtils.convertBooleanOption(passedOptions?.special); this.capitalize = CliUtils.convertBooleanOption(passedOptions?.capitalize); this.includeNumber = CliUtils.convertBooleanOption(passedOptions?.includeNumber); - this.length = passedOptions?.length != null ? parseInt(passedOptions?.length, null) : 14; + this.ambiguous = CliUtils.convertBooleanOption(passedOptions?.ambiguous); + this.length = CliUtils.convertNumberOption(passedOptions?.length, 14); this.type = passedOptions?.passphrase ? "passphrase" : "password"; - this.separator = passedOptions?.separator == null ? "-" : passedOptions.separator + ""; - this.words = passedOptions?.words != null ? parseInt(passedOptions.words, null) : 3; + this.separator = CliUtils.convertStringOption(passedOptions?.separator, "-"); + this.words = CliUtils.convertNumberOption(passedOptions?.words, 3); + this.minNumber = CliUtils.convertNumberOption(passedOptions?.minNumber, 1); + this.minSpecial = CliUtils.convertNumberOption(passedOptions?.minSpecial, 1); if (!this.uppercase && !this.lowercase && !this.special && !this.number) { this.lowercase = true; diff --git a/apps/cli/src/utils.ts b/apps/cli/src/utils.ts index f8780dbec63..5d77f6d3730 100644 --- a/apps/cli/src/utils.ts +++ b/apps/cli/src/utils.ts @@ -253,4 +253,20 @@ export class CliUtils { static convertBooleanOption(optionValue: any) { return optionValue || optionValue === "" ? true : false; } + + static convertNumberOption(optionValue: any, defaultValue: number) { + try { + if (optionValue != null) { + const numVal = parseInt(optionValue); + return !Number.isNaN(numVal) ? numVal : defaultValue; + } + return defaultValue; + } catch { + return defaultValue; + } + } + + static convertStringOption(optionValue: any, defaultValue: string) { + return optionValue != null ? String(optionValue) : defaultValue; + } } From 182d5bf5ace604f93f17032b580efa399e3b862d Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Mon, 4 Sep 2023 22:07:14 -0400 Subject: [PATCH 087/135] [PM-3758] Handle user decryption options from pre-TDE server response (#6180) * Mapped pre-TDE server response to UserDecryptionOptions. * Updated logic on SsoLoginStrategy to match account. * Linting. * Adjusted tests. * Fixed tests. --- .../login-strategies/login.strategy.spec.ts | 11 +--- .../auth/login-strategies/login.strategy.ts | 4 +- .../sso-login.strategy.spec.ts | 56 ++++++++++++++++++- .../login-strategies/sso-login.strategy.ts | 24 +++++--- .../src/platform/models/domain/account.ts | 50 +++++++++++------ 5 files changed, 107 insertions(+), 38 deletions(-) diff --git a/libs/common/src/auth/login-strategies/login.strategy.spec.ts b/libs/common/src/auth/login-strategies/login.strategy.spec.ts index f7128b35dfb..735135f4061 100644 --- a/libs/common/src/auth/login-strategies/login.strategy.spec.ts +++ b/libs/common/src/auth/login-strategies/login.strategy.spec.ts @@ -41,10 +41,7 @@ import { IdentityCaptchaResponse } from "../models/response/identity-captcha.res import { IdentityTokenResponse } from "../models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response"; import { MasterPasswordPolicyResponse } from "../models/response/master-password-policy.response"; -import { - IUserDecryptionOptionsServerResponse, - UserDecryptionOptionsResponse, -} from "../models/response/user-decryption-options/user-decryption-options.response"; +import { IUserDecryptionOptionsServerResponse } from "../models/response/user-decryption-options/user-decryption-options.response"; import { PasswordLogInStrategy } from "./password-login.strategy"; @@ -65,10 +62,6 @@ const name = "NAME"; const defaultUserDecryptionOptionsServerResponse: IUserDecryptionOptionsServerResponse = { HasMasterPassword: true, }; -const userDecryptionOptions = new UserDecryptionOptionsResponse( - defaultUserDecryptionOptionsServerResponse -); -const acctDecryptionOptions = AccountDecryptionOptions.fromResponse(userDecryptionOptions); const decodedToken = { sub: userId, @@ -197,7 +190,7 @@ describe("LogInStrategy", () => { }, }, keys: new AccountKeys(), - decryptionOptions: acctDecryptionOptions, + decryptionOptions: AccountDecryptionOptions.fromResponse(idTokenResponse), }) ); expect(messagingService.send).toHaveBeenCalledWith("loggedIn"); diff --git a/libs/common/src/auth/login-strategies/login.strategy.ts b/libs/common/src/auth/login-strategies/login.strategy.ts index 7bc83580164..6e51f215012 100644 --- a/libs/common/src/auth/login-strategies/login.strategy.ts +++ b/libs/common/src/auth/login-strategies/login.strategy.ts @@ -143,9 +143,7 @@ export abstract class LogInStrategy { }, }, keys: accountKeys, - decryptionOptions: AccountDecryptionOptions.fromResponse( - tokenResponse.userDecryptionOptions - ), + decryptionOptions: AccountDecryptionOptions.fromResponse(tokenResponse), adminAuthRequest: adminAuthRequest?.toJSON(), }) ); diff --git a/libs/common/src/auth/login-strategies/sso-login.strategy.spec.ts b/libs/common/src/auth/login-strategies/sso-login.strategy.spec.ts index 099a3a02a2b..f078a7b86b1 100644 --- a/libs/common/src/auth/login-strategies/sso-login.strategy.spec.ts +++ b/libs/common/src/auth/login-strategies/sso-login.strategy.spec.ts @@ -266,7 +266,61 @@ describe("SsoLogInStrategy", () => { describe("Key Connector", () => { let tokenResponse: IdentityTokenResponse; beforeEach(() => { - tokenResponse = identityTokenResponseFactory(null, { HasMasterPassword: false }); + tokenResponse = identityTokenResponseFactory(null, { + HasMasterPassword: false, + KeyConnectorOption: { KeyConnectorUrl: keyConnectorUrl }, + }); + tokenResponse.keyConnectorUrl = keyConnectorUrl; + }); + + it("gets and sets the master key if Key Connector is enabled and the user doesn't have a master password", async () => { + const masterKey = new SymmetricCryptoKey( + new Uint8Array(64).buffer as CsprngArray + ) as MasterKey; + + apiService.postIdentityToken.mockResolvedValue(tokenResponse); + cryptoService.getMasterKey.mockResolvedValue(masterKey); + + await ssoLogInStrategy.logIn(credentials); + + expect(keyConnectorService.setMasterKeyFromUrl).toHaveBeenCalledWith(keyConnectorUrl); + }); + + it("converts new SSO user with no master password to Key Connector on first login", async () => { + tokenResponse.key = null; + + apiService.postIdentityToken.mockResolvedValue(tokenResponse); + + await ssoLogInStrategy.logIn(credentials); + + expect(keyConnectorService.convertNewSsoUserToKeyConnector).toHaveBeenCalledWith( + tokenResponse, + ssoOrgId + ); + }); + + it("decrypts and sets the user key if Key Connector is enabled and the user doesn't have a master password", async () => { + const userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey; + const masterKey = new SymmetricCryptoKey( + new Uint8Array(64).buffer as CsprngArray + ) as MasterKey; + + apiService.postIdentityToken.mockResolvedValue(tokenResponse); + cryptoService.getMasterKey.mockResolvedValue(masterKey); + cryptoService.decryptUserKeyWithMasterKey.mockResolvedValue(userKey); + + await ssoLogInStrategy.logIn(credentials); + + expect(cryptoService.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(masterKey); + expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey); + }); + }); + + describe("Key Connector Pre-TDE", () => { + let tokenResponse: IdentityTokenResponse; + beforeEach(() => { + tokenResponse = identityTokenResponseFactory(); + tokenResponse.userDecryptionOptions = null; tokenResponse.keyConnectorUrl = keyConnectorUrl; }); diff --git a/libs/common/src/auth/login-strategies/sso-login.strategy.ts b/libs/common/src/auth/login-strategies/sso-login.strategy.ts index 3e9a7e33f33..09dbca72fea 100644 --- a/libs/common/src/auth/login-strategies/sso-login.strategy.ts +++ b/libs/common/src/auth/login-strategies/sso-login.strategy.ts @@ -101,16 +101,22 @@ export class SsoLogInStrategy extends LogInStrategy { private shouldSetMasterKeyFromKeyConnector(tokenResponse: IdentityTokenResponse): boolean { const userDecryptionOptions = tokenResponse?.userDecryptionOptions; - // If the user has a master password, this means that they need to migrate to Key Connector, so we won't set the key here. - // We default to false here because old server versions won't have hasMasterPassword and in that case we want to rely solely on the keyConnectorUrl. - // TODO: remove null default after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3537) - const userHasMasterPassword = userDecryptionOptions?.hasMasterPassword ?? false; + if (userDecryptionOptions != null) { + const userHasMasterPassword = userDecryptionOptions.hasMasterPassword; + const userHasKeyConnectorUrl = + userDecryptionOptions.keyConnectorOption?.keyConnectorUrl != null; - const keyConnectorUrl = this.getKeyConnectorUrl(tokenResponse); - - // In order for us to set the master key from Key Connector, we need to have a Key Connector URL - // and the user must not have a master password. - return keyConnectorUrl != null && !userHasMasterPassword; + // In order for us to set the master key from Key Connector, we need to have a Key Connector URL + // and the user must not have a master password. + return userHasKeyConnectorUrl && !userHasMasterPassword; + } else { + // In pre-TDE versions of the server, the userDecryptionOptions will not be present. + // In this case, we can determine if the user has a master password and has a Key Connector URL by + // just checking the keyConnectorUrl property. This is because the server short-circuits on the response + // and will not pass back the URL in the response if the user has a master password. + // TODO: remove compatibility check after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3537) + return tokenResponse.keyConnectorUrl != null; + } } private getKeyConnectorUrl(tokenResponse: IdentityTokenResponse): string { diff --git a/libs/common/src/platform/models/domain/account.ts b/libs/common/src/platform/models/domain/account.ts index 95a5a899129..09dc6971dcf 100644 --- a/libs/common/src/platform/models/domain/account.ts +++ b/libs/common/src/platform/models/domain/account.ts @@ -11,7 +11,7 @@ import { EnvironmentUrls } from "../../../auth/models/domain/environment-urls"; import { ForceResetPasswordReason } from "../../../auth/models/domain/force-reset-password-reason"; import { KeyConnectorUserDecryptionOption } from "../../../auth/models/domain/user-decryption-options/key-connector-user-decryption-option"; import { TrustedDeviceUserDecryptionOption } from "../../../auth/models/domain/user-decryption-options/trusted-device-user-decryption-option"; -import { UserDecryptionOptionsResponse } from "../../../auth/models/response/user-decryption-options/user-decryption-options.response"; +import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response"; import { KdfType, UriMatchType } from "../../../enums"; import { EventData } from "../../../models/data/event.data"; import { GeneratedPasswordHistory } from "../../../tools/generator/password"; @@ -311,28 +311,46 @@ export class AccountDecryptionOptions { // return this.keyConnectorOption !== null && this.keyConnectorOption !== undefined; // } - static fromResponse(response: UserDecryptionOptionsResponse): AccountDecryptionOptions { + static fromResponse(response: IdentityTokenResponse): AccountDecryptionOptions { if (response == null) { return null; } const accountDecryptionOptions = new AccountDecryptionOptions(); - accountDecryptionOptions.hasMasterPassword = response.hasMasterPassword; - if (response.trustedDeviceOption) { - accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption( - response.trustedDeviceOption.hasAdminApproval, - response.trustedDeviceOption.hasLoginApprovingDevice, - response.trustedDeviceOption.hasManageResetPasswordPermission - ); + if (response.userDecryptionOptions) { + // If the response has userDecryptionOptions, this means it's on a post-TDE server version and can interrogate + // the new decryption options. + const responseOptions = response.userDecryptionOptions; + accountDecryptionOptions.hasMasterPassword = responseOptions.hasMasterPassword; + + if (responseOptions.trustedDeviceOption) { + accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption( + responseOptions.trustedDeviceOption.hasAdminApproval, + responseOptions.trustedDeviceOption.hasLoginApprovingDevice, + responseOptions.trustedDeviceOption.hasManageResetPasswordPermission + ); + } + + if (responseOptions.keyConnectorOption) { + accountDecryptionOptions.keyConnectorOption = new KeyConnectorUserDecryptionOption( + responseOptions.keyConnectorOption.keyConnectorUrl + ); + } + } else { + // If the response does not have userDecryptionOptions, this means it's on a pre-TDE server version and so + // we must base our decryption options on the presence of the keyConnectorUrl. + // Note that the presence of keyConnectorUrl implies that the user does not have a master password, as in pre-TDE + // server versions, a master password short-circuited the addition of the keyConnectorUrl to the response. + // TODO: remove this check after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3537) + const usingKeyConnector = response.keyConnectorUrl != null; + accountDecryptionOptions.hasMasterPassword = !usingKeyConnector; + if (usingKeyConnector) { + accountDecryptionOptions.keyConnectorOption = new KeyConnectorUserDecryptionOption( + response.keyConnectorUrl + ); + } } - - if (response.keyConnectorOption) { - accountDecryptionOptions.keyConnectorOption = new KeyConnectorUserDecryptionOption( - response.keyConnectorOption.keyConnectorUrl - ); - } - return accountDecryptionOptions; } From b78d17aa62a07341c3c21bc6ca541eb7510115b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:55:37 -0600 Subject: [PATCH 088/135] Bump Desktop version to 2023.8.4 (#6192) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/package.json | 2 +- apps/desktop/src/package-lock.json | 4 ++-- apps/desktop/src/package.json | 2 +- package-lock.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 82c77156c12..6855485510a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2023.8.3", + "version": "2023.8.4", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 54af2baea1e..c838b242b50 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2023.8.3", + "version": "2023.8.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2023.8.3", + "version": "2023.8.4", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-native": "file:../desktop_native" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index b171046bcef..bbef6bf36f5 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2023.8.3", + "version": "2023.8.4", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/package-lock.json b/package-lock.json index 0b364d58ed7..3ce3baf3174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -231,7 +231,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2023.8.3", + "version": "2023.8.4", "hasInstallScript": true, "license": "GPL-3.0" }, From 255a7381b3cd7686d914abde248ea66e1f618e4f Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Tue, 5 Sep 2023 21:48:34 +0200 Subject: [PATCH 089/135] [PM-3609] [Tech-Debt] Add types to password and username generator (#6090) * Create and use GeneratorOptions Selection between `password`and `username` * Use PasswordGeneratorOptions * Declare and use UsernameGeneratorOptions --- .../components/generator.component.ts | 17 +++++++++----- .../platform/abstractions/state.service.ts | 22 +++++++++++++------ .../src/platform/models/domain/account.ts | 13 +++++++---- .../src/platform/services/state.service.ts | 22 +++++++++++++------ .../src/tools/generator/generator-options.ts | 3 +++ .../src/tools/generator/password/index.ts | 1 + .../src/tools/generator/username/index.ts | 1 + .../username/username-generation-options.ts | 19 ++++++++++++++++ ...username-generation.service.abstraction.ts | 16 ++++++++------ .../username/username-generation.service.ts | 17 +++++++------- 10 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 libs/common/src/tools/generator/generator-options.ts create mode 100644 libs/common/src/tools/generator/username/username-generation-options.ts diff --git a/libs/angular/src/tools/generator/components/generator.component.ts b/libs/angular/src/tools/generator/components/generator.component.ts index 9ef73186583..37904473ad9 100644 --- a/libs/angular/src/tools/generator/components/generator.component.ts +++ b/libs/angular/src/tools/generator/components/generator.component.ts @@ -8,8 +8,15 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic 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 { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; -import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username"; +import { GeneratorOptions } from "@bitwarden/common/tools/generator/generator-options"; +import { + PasswordGenerationServiceAbstraction, + PasswordGeneratorOptions, +} from "@bitwarden/common/tools/generator/password"; +import { + UsernameGenerationServiceAbstraction, + UsernameGeneratorOptions, +} from "@bitwarden/common/tools/generator/username"; @Directive() export class GeneratorComponent implements OnInit { @@ -24,8 +31,8 @@ export class GeneratorComponent implements OnInit { subaddressOptions: any[]; catchallOptions: any[]; forwardOptions: EmailForwarderOptions[]; - usernameOptions: any = {}; - passwordOptions: any = {}; + usernameOptions: UsernameGeneratorOptions = {}; + passwordOptions: PasswordGeneratorOptions = {}; username = "-"; password = "-"; showOptions = false; @@ -118,7 +125,7 @@ export class GeneratorComponent implements OnInit { } async typeChanged() { - await this.stateService.setGeneratorOptions({ type: this.type }); + await this.stateService.setGeneratorOptions({ type: this.type } as GeneratorOptions); if (this.regenerateWithoutButtonPress()) { await this.regenerate(); } diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 82813718de3..571dad6478e 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -13,7 +13,9 @@ import { BiometricKey } from "../../auth/types/biometric-key"; import { KdfType, ThemeType, UriMatchType } from "../../enums"; import { EventData } from "../../models/data/event.data"; import { WindowState } from "../../models/domain/window-state"; -import { GeneratedPasswordHistory } from "../../tools/generator/password"; +import { GeneratorOptions } from "../../tools/generator/generator-options"; +import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password"; +import { UsernameGeneratorOptions } from "../../tools/generator/username"; import { SendData } from "../../tools/send/models/data/send.data"; import { SendView } from "../../tools/send/models/view/send.view"; import { CipherData } from "../../vault/models/data/cipher.data"; @@ -439,12 +441,18 @@ export abstract class StateService { value: { [id: string]: OrganizationData }, options?: StorageOptions ) => Promise; - getPasswordGenerationOptions: (options?: StorageOptions) => Promise; - setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise; - getUsernameGenerationOptions: (options?: StorageOptions) => Promise; - setUsernameGenerationOptions: (value: any, options?: StorageOptions) => Promise; - getGeneratorOptions: (options?: StorageOptions) => Promise; - setGeneratorOptions: (value: any, options?: StorageOptions) => Promise; + getPasswordGenerationOptions: (options?: StorageOptions) => Promise; + setPasswordGenerationOptions: ( + value: PasswordGeneratorOptions, + options?: StorageOptions + ) => Promise; + getUsernameGenerationOptions: (options?: StorageOptions) => Promise; + setUsernameGenerationOptions: ( + value: UsernameGeneratorOptions, + options?: StorageOptions + ) => Promise; + getGeneratorOptions: (options?: StorageOptions) => Promise; + setGeneratorOptions: (value: GeneratorOptions, options?: StorageOptions) => Promise; /** * Gets the user's Pin, encrypted by the user key */ diff --git a/libs/common/src/platform/models/domain/account.ts b/libs/common/src/platform/models/domain/account.ts index 09dc6971dcf..6d85d6501fe 100644 --- a/libs/common/src/platform/models/domain/account.ts +++ b/libs/common/src/platform/models/domain/account.ts @@ -14,7 +14,12 @@ import { TrustedDeviceUserDecryptionOption } from "../../../auth/models/domain/u import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response"; import { KdfType, UriMatchType } from "../../../enums"; import { EventData } from "../../../models/data/event.data"; -import { GeneratedPasswordHistory } from "../../../tools/generator/password"; +import { GeneratorOptions } from "../../../tools/generator/generator-options"; +import { + GeneratedPasswordHistory, + PasswordGeneratorOptions, +} from "../../../tools/generator/password"; +import { UsernameGeneratorOptions } from "../../../tools/generator/username/username-generation-options"; import { SendData } from "../../../tools/send/models/data/send.data"; import { SendView } from "../../../tools/send/models/view/send.view"; import { DeepJsonify } from "../../../types/deep-jsonify"; @@ -235,9 +240,9 @@ export class AccountSettings { equivalentDomains?: any; minimizeOnCopyToClipboard?: boolean; neverDomains?: { [id: string]: any }; - passwordGenerationOptions?: any; - usernameGenerationOptions?: any; - generatorOptions?: any; + passwordGenerationOptions?: PasswordGeneratorOptions; + usernameGenerationOptions?: UsernameGeneratorOptions; + generatorOptions?: GeneratorOptions; pinKeyEncryptedUserKey?: EncryptedString; pinKeyEncryptedUserKeyEphemeral?: EncryptedString; protectedPin?: string; diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index 5fdf40e8458..d0983448d62 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -22,7 +22,9 @@ import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum"; import { EventData } from "../../models/data/event.data"; import { WindowState } from "../../models/domain/window-state"; import { migrate } from "../../state-migrations"; -import { GeneratedPasswordHistory } from "../../tools/generator/password"; +import { GeneratorOptions } from "../../tools/generator/generator-options"; +import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password"; +import { UsernameGeneratorOptions } from "../../tools/generator/username"; import { SendData } from "../../tools/send/models/data/send.data"; import { SendView } from "../../tools/send/models/view/send.view"; import { CipherData } from "../../vault/models/data/cipher.data"; @@ -2367,13 +2369,16 @@ export class StateService< ); } - async getPasswordGenerationOptions(options?: StorageOptions): Promise { + async getPasswordGenerationOptions(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) )?.settings?.passwordGenerationOptions; } - async setPasswordGenerationOptions(value: any, options?: StorageOptions): Promise { + async setPasswordGenerationOptions( + value: PasswordGeneratorOptions, + options?: StorageOptions + ): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) ); @@ -2384,13 +2389,16 @@ export class StateService< ); } - async getUsernameGenerationOptions(options?: StorageOptions): Promise { + async getUsernameGenerationOptions(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) )?.settings?.usernameGenerationOptions; } - async setUsernameGenerationOptions(value: any, options?: StorageOptions): Promise { + async setUsernameGenerationOptions( + value: UsernameGeneratorOptions, + options?: StorageOptions + ): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) ); @@ -2401,13 +2409,13 @@ export class StateService< ); } - async getGeneratorOptions(options?: StorageOptions): Promise { + async getGeneratorOptions(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) )?.settings?.generatorOptions; } - async setGeneratorOptions(value: any, options?: StorageOptions): Promise { + async setGeneratorOptions(value: GeneratorOptions, options?: StorageOptions): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) ); diff --git a/libs/common/src/tools/generator/generator-options.ts b/libs/common/src/tools/generator/generator-options.ts new file mode 100644 index 00000000000..4f8eb293ab5 --- /dev/null +++ b/libs/common/src/tools/generator/generator-options.ts @@ -0,0 +1,3 @@ +export type GeneratorOptions = { + type?: "password" | "username"; +}; diff --git a/libs/common/src/tools/generator/password/index.ts b/libs/common/src/tools/generator/password/index.ts index 4dafe20d3aa..bacc2c0c70d 100644 --- a/libs/common/src/tools/generator/password/index.ts +++ b/libs/common/src/tools/generator/password/index.ts @@ -1,3 +1,4 @@ +export { PasswordGeneratorOptions } from "./password-generator-options"; export { PasswordGenerationServiceAbstraction } from "./password-generation.service.abstraction"; export { PasswordGenerationService } from "./password-generation.service"; export { GeneratedPasswordHistory } from "./generated-password-history"; diff --git a/libs/common/src/tools/generator/username/index.ts b/libs/common/src/tools/generator/username/index.ts index b4f73a29edb..c4197b4344f 100644 --- a/libs/common/src/tools/generator/username/index.ts +++ b/libs/common/src/tools/generator/username/index.ts @@ -1,2 +1,3 @@ +export { UsernameGeneratorOptions } from "./username-generation-options"; export { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction"; export { UsernameGenerationService } from "./username-generation.service"; diff --git a/libs/common/src/tools/generator/username/username-generation-options.ts b/libs/common/src/tools/generator/username/username-generation-options.ts new file mode 100644 index 00000000000..96b6e2ef1be --- /dev/null +++ b/libs/common/src/tools/generator/username/username-generation-options.ts @@ -0,0 +1,19 @@ +export type UsernameGeneratorOptions = { + type?: "word" | "subaddress" | "catchall" | "forwarded"; + wordCapitalize?: boolean; + wordIncludeNumber?: boolean; + subaddressType?: "random" | "website-name"; + subaddressEmail?: string; + catchallType?: "random" | "website-name"; + catchallDomain?: string; + website?: string; + forwardedService?: string; + forwardedAnonAddyApiToken?: string; + forwardedAnonAddyDomain?: string; + forwardedDuckDuckGoToken?: string; + forwardedFirefoxApiToken?: string; + forwardedFastmailApiToken?: string; + forwardedForwardEmailApiToken?: string; + forwardedForwardEmailDomain?: string; + forwardedSimpleLoginApiKey?: string; +}; diff --git a/libs/common/src/tools/generator/username/username-generation.service.abstraction.ts b/libs/common/src/tools/generator/username/username-generation.service.abstraction.ts index 52accf7d8ca..05affef0e2f 100644 --- a/libs/common/src/tools/generator/username/username-generation.service.abstraction.ts +++ b/libs/common/src/tools/generator/username/username-generation.service.abstraction.ts @@ -1,9 +1,11 @@ +import { UsernameGeneratorOptions } from "./username-generation-options"; + export abstract class UsernameGenerationServiceAbstraction { - generateUsername: (options: any) => Promise; - generateWord: (options: any) => Promise; - generateSubaddress: (options: any) => Promise; - generateCatchall: (options: any) => Promise; - generateForwarded: (options: any) => Promise; - getOptions: () => Promise; - saveOptions: (options: any) => Promise; + generateUsername: (options: UsernameGeneratorOptions) => Promise; + generateWord: (options: UsernameGeneratorOptions) => Promise; + generateSubaddress: (options: UsernameGeneratorOptions) => Promise; + generateCatchall: (options: UsernameGeneratorOptions) => Promise; + generateForwarded: (options: UsernameGeneratorOptions) => Promise; + getOptions: () => Promise; + saveOptions: (options: UsernameGeneratorOptions) => Promise; } diff --git a/libs/common/src/tools/generator/username/username-generation.service.ts b/libs/common/src/tools/generator/username/username-generation.service.ts index 3ff9884331d..b1fed147db0 100644 --- a/libs/common/src/tools/generator/username/username-generation.service.ts +++ b/libs/common/src/tools/generator/username/username-generation.service.ts @@ -13,9 +13,10 @@ import { ForwarderOptions, SimpleLoginForwarder, } from "./email-forwarders"; +import { UsernameGeneratorOptions } from "./username-generation-options"; import { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction"; -const DefaultOptions = { +const DefaultOptions: UsernameGeneratorOptions = { type: "word", wordCapitalize: true, wordIncludeNumber: true, @@ -33,7 +34,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr private apiService: ApiService ) {} - generateUsername(options: any): Promise { + generateUsername(options: UsernameGeneratorOptions): Promise { if (options.type === "catchall") { return this.generateCatchall(options); } else if (options.type === "subaddress") { @@ -45,7 +46,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr } } - async generateWord(options: any): Promise { + async generateWord(options: UsernameGeneratorOptions): Promise { const o = Object.assign({}, DefaultOptions, options); if (o.wordCapitalize == null) { @@ -67,7 +68,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr return word; } - async generateSubaddress(options: any): Promise { + async generateSubaddress(options: UsernameGeneratorOptions): Promise { const o = Object.assign({}, DefaultOptions, options); const subaddressEmail = o.subaddressEmail; @@ -94,7 +95,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr return emailBeginning + "+" + subaddressString + "@" + emailEnding; } - async generateCatchall(options: any): Promise { + async generateCatchall(options: UsernameGeneratorOptions): Promise { const o = Object.assign({}, DefaultOptions, options); if (o.catchallDomain == null || o.catchallDomain === "") { @@ -113,7 +114,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr return startString + "@" + o.catchallDomain; } - async generateForwarded(options: any): Promise { + async generateForwarded(options: UsernameGeneratorOptions): Promise { const o = Object.assign({}, DefaultOptions, options); if (o.forwardedService == null) { @@ -152,7 +153,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr return forwarder.generate(this.apiService, forwarderOptions); } - async getOptions(): Promise { + async getOptions(): Promise { let options = await this.stateService.getUsernameGenerationOptions(); if (options == null) { options = Object.assign({}, DefaultOptions); @@ -163,7 +164,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr return options; } - async saveOptions(options: any) { + async saveOptions(options: UsernameGeneratorOptions) { await this.stateService.setUsernameGenerationOptions(options); } From d6aa85af6613b93a9823ae432b698f8dfcfdc528 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:52:03 -0400 Subject: [PATCH 090/135] Update Version Bump workflow inputs (#6143) --- .github/workflows/version-auto-bump.yml | 3 +- .github/workflows/version-bump.yml | 113 ++++++++++++++---------- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 857099db511..7b1a787d946 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -44,4 +44,5 @@ jobs: uses: ./.github/workflows/version-bump.yml with: version_number: ${{ needs.setup.outputs.version_number }} - client: "Desktop" + bump_desktop: true + secrets: inherit diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 420ef456ec0..563facdb40c 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -4,16 +4,22 @@ name: Version Bump on: workflow_dispatch: inputs: - client: - description: "Client Project" - required: true - type: choice - options: - - Browser - - CLI - - Desktop - - Web - - All + bump_browser: + description: "Browser Project Version Bump" + type: boolean + default: false + bump_cli: + description: "CLI Project Version Bump" + type: boolean + default: false + bump_desktop: + description: "Desktop Project Version Bump" + type: boolean + default: false + bump_web: + description: "Web Project Version Bump" + type: boolean + default: false version_number: description: "New Version" required: true @@ -23,9 +29,10 @@ on: version_number: required: true type: string - client: - required: true - type: string + bump_desktop: + description: "Desktop Project Version Bump" + type: boolean + default: false defaults: run: @@ -33,8 +40,8 @@ defaults: jobs: bump_version: - name: "Bump ${{ github.event.inputs.client }} Version" - runs-on: ubuntu-20.04 + name: "Bump Version" + runs-on: ubuntu-22.04 steps: - name: Checkout Branch uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 @@ -42,7 +49,7 @@ jobs: - name: Login to Azure - Prod Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: - creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets @@ -62,13 +69,27 @@ jobs: - name: Create Version Branch id: branch env: - CLIENT_NAME: ${{ github.event.inputs.client }} - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: | - CLIENT=$(python -c "print('$CLIENT_NAME'.lower())") - echo "client=$CLIENT" >> $GITHUB_OUTPUT + CLIENTS=() + if [[ ${{ inputs.bump_browser }} == true ]]; then + CLIENTS+=("browser") + fi + if [[ ${{ inputs.bump_cli }} == true ]]; then + CLIENTS+=("cli") + fi + if [[ ${{ inputs.bump_desktop }} == true ]]; then + CLIENTS+=("desktop") + fi + if [[ ${{ inputs.bump_web }} == true ]]; then + CLIENTS+=("web") + fi + printf -v joined '%s,' "${CLIENTS[@]}" + echo "client=${joined%,}" >> $GITHUB_OUTPUT - git switch -c ${CLIENT}_version_bump_${VERSION} + BRANCH=version_bump_${VERSION}_${GITHUB_SHA:0:7} + echo "branch=$BRANCH" >> $GITHUB_OUTPUT + git switch -c ${BRANCH} ######################## # VERSION BUMP SECTION # @@ -76,27 +97,27 @@ jobs: ### Browser - name: Bump Browser Version - if: ${{ github.event.inputs.client == 'Browser' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_browser == true }} env: - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: npm version --workspace=@bitwarden/browser ${VERSION} - name: Bump Browser Version - Manifest - if: ${{ github.event.inputs.client == 'Browser' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_browser == true }} uses: bitwarden/gh-actions/version-bump@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: - version: ${{ github.event.inputs.version_number }} + version: ${{ inputs.version_number }} file_path: "apps/browser/src/manifest.json" - name: Bump Browser Version - Manifest v3 - if: ${{ github.event.inputs.client == 'Browser' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_browser == true }} uses: bitwarden/gh-actions/version-bump@67ab95d7a466bcefdedf3f93cbc10bcff436edfe with: - version: ${{ github.event.inputs.version_number }} + version: ${{ inputs.version_number }} file_path: "apps/browser/src/manifest.v3.json" - name: Run Prettier after Browser Version Bump - if: ${{ github.event.inputs.client == 'Browser' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_browser == true }} run: | npm install -g prettier prettier --write apps/browser/src/manifest.json @@ -104,30 +125,30 @@ jobs: ### CLI - name: Bump CLI Version - if: ${{ github.event.inputs.client == 'CLI' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_cli == true }} env: - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: npm version --workspace=@bitwarden/cli ${VERSION} ### Desktop - name: Bump Desktop Version - Root - if: ${{ github.event.inputs.client == 'Desktop' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_desktop == true }} env: - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: npm version --workspace=@bitwarden/desktop ${VERSION} - name: Bump Desktop Version - App - if: ${{ github.event.inputs.client == 'Desktop' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_desktop == true }} env: - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: npm version ${VERSION} working-directory: "apps/desktop/src" ### Web - name: Bump Web Version - if: ${{ github.event.inputs.client == 'Web' || github.event.inputs.client == 'All' }} + if: ${{ inputs.bump_web == true }} env: - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: npm version --workspace=@bitwarden/web-vault ${VERSION} ######################## @@ -151,27 +172,26 @@ jobs: if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} env: CLIENT: ${{ steps.branch.outputs.client }} - VERSION: ${{ github.event.inputs.version_number }} + VERSION: ${{ inputs.version_number }} run: git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a - name: Push changes if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} env: - CLIENT: ${{ steps.branch.outputs.client }} - VERSION: ${{ github.event.inputs.version_number }} - run: git push -u origin ${CLIENT}_version_bump_${VERSION} + BRANCH: ${{ steps.branch.outputs.branch }} + run: git push -u origin ${BRANCH} - name: Create Bump Version PR if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} env: - PR_BRANCH: "${{ steps.branch.outputs.client }}_version_bump_${{ github.event.inputs.version_number }}" - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" BASE_BRANCH: master - TITLE: "Bump ${{ github.event.inputs.client }} version to ${{ github.event.inputs.version_number }}" + BRANCH: ${{ steps.branch.outputs.branch }} + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + TITLE: "Bump ${{ steps.branch.outputs.client }} version to ${{ inputs.version_number }}" run: | gh pr create --title "$TITLE" \ - --base "$BASE" \ - --head "$PR_BRANCH" \ + --base "$BASE_BRANCH" \ + --head "$BRANCH" \ --label "version update" \ --label "automated pr" \ --body " @@ -183,5 +203,4 @@ jobs: - [X] Other ## Objective - Automated ${{ github.event.inputs.client }} version bump to ${{ github.event.inputs.version_number }}" - + Automated ${{ steps.branch.outputs.client }} version bump to ${{ inputs.version_number }}" From 1bd1127b61b5c1b784bc3c955cef6b9b889f97b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 18:44:46 -0400 Subject: [PATCH 091/135] Bumped browser,web version to 2023.8.3 (#6197) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/package.json | 2 +- apps/browser/src/manifest.json | 2 +- apps/browser/src/manifest.v3.json | 2 +- apps/web/package.json | 2 +- package-lock.json | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/browser/package.json b/apps/browser/package.json index 2e866653cd3..980eb3258a2 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2023.8.2", + "version": "2023.8.3", "scripts": { "build": "webpack", "build:mv3": "cross-env MANIFEST_VERSION=3 webpack", diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 7e471c39201..1e7b3a3139f 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2023.8.2", + "version": "2023.8.3", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 321fdb0beb4..f10ac7f3824 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2023.8.2", + "version": "2023.8.3", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/web/package.json b/apps/web/package.json index 1624e0d491d..d3c4d286f08 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2023.8.2", + "version": "2023.8.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 3ce3baf3174..f1bb3b7d84a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -191,7 +191,7 @@ }, "apps/browser": { "name": "@bitwarden/browser", - "version": "2023.8.2" + "version": "2023.8.3" }, "apps/cli": { "name": "@bitwarden/cli", @@ -261,7 +261,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2023.8.2" + "version": "2023.8.3" }, "libs/angular": { "name": "@bitwarden/angular", From 864818c2d30165c6ad5f4cf1e19ca56ace1c5b22 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Wed, 6 Sep 2023 09:51:40 -0400 Subject: [PATCH 092/135] Browser Build/Release Workflows - Change runners to linux (#6193) --- .github/workflows/build-browser.yml | 44 +++++++++++++++------------ .github/workflows/release-browser.yml | 8 ++--- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 412f166629e..05b89d66c33 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -38,7 +38,7 @@ defaults: jobs: cloc: name: CLOC - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repo uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 @@ -54,7 +54,7 @@ jobs: setup: name: Setup - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: repo_url: ${{ steps.gen_vars.outputs.repo_url }} adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }} @@ -71,7 +71,7 @@ jobs: locales-test: name: Locales Test - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - setup defaults: @@ -108,7 +108,7 @@ jobs: build: name: Build - runs-on: windows-2019 + runs-on: ubuntu-22.04 needs: - setup - locales-test @@ -137,6 +137,7 @@ jobs: run: | node --version npm --version + node-gyp --version - name: NPM setup run: npm ci @@ -152,24 +153,27 @@ jobs: run: gulp ci - name: Build sources for reviewers - shell: cmd run: | - REM Remove ".git" directory - rmdir /S /Q ".git" + # Include hidden files in glob copy + shopt -s dotglob - REM Copy root level files to source directory + # Remove ".git" directory + rm -r .git + + # Copy root level files to source directory mkdir browser-source - copy * browser-source + FILES=$(find . -maxdepth 1 -type f) + for FILE in $FILES; do cp "$FILE" browser-source/; done - REM Copy apps\browser to Browser source directory - mkdir browser-source\apps\browser - xcopy apps\browser\* browser-source\apps\browser /E + # Copy apps/browser to Browser source directory + mkdir -p browser-source/apps/browser + cp -r apps/browser/* browser-source/apps/browser - REM Copy libs to Browser source directory - mkdir browser-source\libs - xcopy libs\* browser-source\libs /E + # Copy libs to Browser source directory + mkdir browser-source/libs + cp -r libs/* browser-source/libs - call 7z a browser-source.zip "browser-source\*" + zip -r browser-source.zip browser-source working-directory: ./ - name: Upload Opera artifact @@ -339,7 +343,7 @@ jobs: crowdin-push: name: Crowdin Push if: github.ref == 'refs/heads/master' - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - build - build-safari @@ -354,7 +358,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@37ffa14164a7308bc273829edfe75c97cd562375 with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" @@ -374,7 +378,7 @@ jobs: check-failures: name: Check for failures if: always() - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - cloc - setup @@ -416,7 +420,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@37ffa14164a7308bc273829edfe75c97cd562375 with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index 20f3f7efac5..407f81deb60 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -22,7 +22,7 @@ defaults: jobs: setup: name: Setup - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: release-version: ${{ steps.version.outputs.version }} steps: @@ -41,7 +41,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/release-version-check@58a2fdfbd3f1fc7e6727bc5dc51d159f4df07072 with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -52,7 +52,7 @@ jobs: locales-test: name: Locales Test - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: setup steps: - name: Checkout repo @@ -86,7 +86,7 @@ jobs: release: name: Create GitHub Release - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - setup - locales-test From e8a5c5b337da1e70cc094011a651d88552182276 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Wed, 6 Sep 2023 16:12:14 +0200 Subject: [PATCH 093/135] [PM-3586] Fix short MP not showing minLength (#6086) * Fix short MP not showing minLength Added path to include the mininum password length defined as const in our Utils * Introduce previousMinimumPasswordLength and use a minLength for MP * Rename previousMinimumPasswordLength to originalMinimumPasswordLength --- libs/angular/src/auth/components/login.component.ts | 7 ++++++- libs/common/src/platform/misc/utils.ts | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/angular/src/auth/components/login.component.ts b/libs/angular/src/auth/components/login.component.ts index 33b3dc2364e..63e8dcc3721 100644 --- a/libs/angular/src/auth/components/login.component.ts +++ b/libs/angular/src/auth/components/login.component.ts @@ -42,7 +42,10 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit formGroup = this.formBuilder.group({ email: ["", [Validators.required, Validators.email]], - masterPassword: ["", [Validators.required, Validators.minLength(8)]], + masterPassword: [ + "", + [Validators.required, Validators.minLength(Utils.originalMinimumPasswordLength)], + ], rememberEmail: [false], }); @@ -278,6 +281,8 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit switch (error.errorName) { case "email": return this.i18nService.t("invalidEmail"); + case "minlength": + return this.i18nService.t("masterPasswordMinlength", Utils.originalMinimumPasswordLength); default: return this.i18nService.t(this.errorTag(error)); } diff --git a/libs/common/src/platform/misc/utils.ts b/libs/common/src/platform/misc/utils.ts index cd1b5fe33aa..6711e78c3b7 100644 --- a/libs/common/src/platform/misc/utils.ts +++ b/libs/common/src/platform/misc/utils.ts @@ -32,6 +32,7 @@ export class Utils { static regexpEmojiPresentation = /(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5-\uDED7\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDD78\uDD7A-\uDDCB\uDDCD-\uDDFF\uDE70-\uDE74\uDE78-\uDE7A\uDE80-\uDE86\uDE90-\uDEA8\uDEB0-\uDEB6\uDEC0-\uDEC2\uDED0-\uDED6])/g; static readonly validHosts: string[] = ["localhost"]; + static readonly originalMinimumPasswordLength = 8; static readonly minimumPasswordLength = 12; static readonly DomainMatchBlacklist = new Map>([ ["google.com", new Set(["script.google.com"])], From 6eb57ff312bac54aa827d6aec0f384a3e781f19f Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:05:24 -0700 Subject: [PATCH 094/135] add route and params to link (#6103) --- .../environment-selector.component.html | 8 ++++++-- .../environment-selector.component.ts | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/components/environment-selector/environment-selector.component.html b/apps/web/src/app/components/environment-selector/environment-selector.component.html index 2984d6a6a3b..5a5bada82df 100644 --- a/apps/web/src/app/components/environment-selector/environment-selector.component.html +++ b/apps/web/src/app/components/environment-selector/environment-selector.component.html @@ -2,7 +2,9 @@ diff --git a/apps/web/src/app/components/environment-selector/environment-selector.component.ts b/apps/web/src/app/components/environment-selector/environment-selector.component.ts index f9c06bbdd3d..2530a7b3642 100644 --- a/apps/web/src/app/components/environment-selector/environment-selector.component.ts +++ b/apps/web/src/app/components/environment-selector/environment-selector.component.ts @@ -1,4 +1,5 @@ import { Component, Input, OnInit } from "@angular/core"; +import { Router } from "@angular/router"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; @@ -13,14 +14,17 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; export class EnvironmentSelectorComponent implements OnInit { constructor( private configService: ConfigServiceAbstraction, - private platformUtilsService: PlatformUtilsService + private platformUtilsService: PlatformUtilsService, + private router: Router ) {} + @Input() hasFlags: boolean; isEuServer: boolean; isUsServer: boolean; showRegionSelector = false; euServerFlagEnabled: boolean; selectedRegionImageName: string; + routeAndParams: string; async ngOnInit() { this.euServerFlagEnabled = await this.configService.getFeatureFlagBool( @@ -31,6 +35,7 @@ export class EnvironmentSelectorComponent implements OnInit { this.isUsServer = domain.includes(RegionDomain.US) || domain.includes(RegionDomain.USQA); this.selectedRegionImageName = this.getRegionImage(); this.showRegionSelector = !this.platformUtilsService.isSelfHost(); + this.routeAndParams = `/#${this.router.url}`; } getRegionImage(): string { From 6f82a9914ba9e8935f7d55865269b44f18afee9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:56:35 -0400 Subject: [PATCH 095/135] Bumped web version to 2023.8.4 (#6206) 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 d3c4d286f08..08698b75a39 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2023.8.3", + "version": "2023.8.4", "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 f1bb3b7d84a..525fbb0a183 100644 --- a/package-lock.json +++ b/package-lock.json @@ -261,7 +261,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2023.8.3" + "version": "2023.8.4" }, "libs/angular": { "name": "@bitwarden/angular", From 86bdfaa7bac2d62e2d61ef7a4fc06edc1ae0fae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Thu, 7 Sep 2023 10:41:59 +0100 Subject: [PATCH 096/135] [AC-1612] Disabled access to the Organization Vault tab if the user only has access to assigned collections (#6140) * [AC-1612] Disabled access to the Organization Vault tab if the user only has access to assigned collections * [AC-1612] Fixed issue that prevented Manager users to access the Organizations tab --- libs/common/src/admin-console/models/domain/organization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index e1e5e8a6e2c..bd3c9036367 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -174,7 +174,7 @@ export class Organization { } get canViewAllCollections() { - return this.canCreateNewCollections || this.canEditAnyCollection || this.canDeleteAnyCollection; + return this.canEditAnyCollection || this.canDeleteAnyCollection; } get canEditAssignedCollections() { From 5f78aeaef2bf5ebe70b5d9a33863c1a4b6e87fe9 Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Thu, 7 Sep 2023 13:49:13 +0100 Subject: [PATCH 097/135] [PM-2805] Migrate add edit send to Component Library (#6004) * Converted add-edit send component dialog into a bit-dialog * Updated Send AddEdit text fields to Component Library * Migrated Share and Options fields to ComponentLibrary on SendAddEdit * Migrated footer buttons to ComponentLibrary on SendAddEdit * Updated web's SendAddEdit component file fields * Replaced file upload with component library * Changed SendAddEdit to use Reactive Forms on web * Changed browser SendAddEdit to use ReactiveForms * Update SendAddEdit on desktop to use ReactiveForms * Added AppA11yTitle to button on web SendAddEdit * Initial efflux-dates web change to ComponentLibrary * Corrected delete button to check if it is in EditMode on SendAddEdit * Using BitLink on options button * Corrected typo on send add edit desktop * Replaced efflux-dates with datetime-local input on SendAddEdit web, browser and desktop * Removed efflux dates * Added firefox custom date popout message on DeletionDate to SendAddEdit browser component * moved desktop's new send data reload from send to SendAddEdit component * removing unnecessary attributes and spans from Send AddEdit web * removed redundant try catch from add edit and unnecessary parameter from close * Added type for date select options * Removed unnecessary classes and swapped bootstrap classes by corresponding tailwind classes * Removed unnecessary code * Added file as required field Submit only closes popup on success * Added pre validations at start of submit * PM-3668 removed expiration date from required * PM-3671 not defaulting maximum access count to 0 * PM-3669 Copying the link from link method * Removed required tag from html and added to formgroup * PM-3679 Checking if is not EditMode before validating if FormGroup file value is set * PM-3691 Moved error validation to web component as browser and desktop need to show popup error * PM-3696 - Disabling hide email when it is unset and has policy to not allow hiding * PM-3694 - Properly setting default value for dates on Desktop when changing from an existing send * Disabling hidden required fields * [PM-3800] Clearing password on new send --- apps/browser/src/popup/app.module.ts | 2 - .../popup/send/efflux-dates.component.html | 217 ------- .../popup/send/efflux-dates.component.ts | 25 - .../popup/send/send-add-edit.component.html | 210 +++++-- .../popup/send/send-add-edit.component.ts | 7 +- apps/desktop/src/app/app.module.ts | 2 - .../app/tools/send/add-edit.component.html | 158 ++--- .../src/app/tools/send/add-edit.component.ts | 15 +- .../tools/send/efflux-dates.component.html | 62 -- .../app/tools/send/efflux-dates.component.ts | 38 -- .../src/app/tools/send/send.component.ts | 6 +- .../src/app/shared/loose-components.module.ts | 3 - .../app/tools/send/add-edit.component.html | 557 +++++++++--------- .../src/app/tools/send/add-edit.component.ts | 40 +- .../tools/send/efflux-dates.component.html | 188 ------ .../app/tools/send/efflux-dates.component.ts | 22 - apps/web/src/app/tools/send/send.component.ts | 29 +- .../src/tools/send/add-edit.component.ts | 224 ++++++- .../src/tools/send/efflux-dates.component.ts | 356 ----------- .../src/form-field/form-field-control.ts | 4 +- 20 files changed, 777 insertions(+), 1388 deletions(-) delete mode 100644 apps/browser/src/tools/popup/send/efflux-dates.component.html delete mode 100644 apps/browser/src/tools/popup/send/efflux-dates.component.ts delete mode 100644 apps/desktop/src/app/tools/send/efflux-dates.component.html delete mode 100644 apps/desktop/src/app/tools/send/efflux-dates.component.ts delete mode 100644 apps/web/src/app/tools/send/efflux-dates.component.html delete mode 100644 apps/web/src/app/tools/send/efflux-dates.component.ts delete mode 100644 libs/angular/src/tools/send/efflux-dates.component.ts diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index f7539d6fa6e..0115768a40f 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -33,7 +33,6 @@ import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password. import { GeneratorComponent } from "../tools/popup/generator/generator.component"; import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; import { SendListComponent } from "../tools/popup/send/components/send-list.component"; -import { EffluxDatesComponent as SendEffluxDatesComponent } from "../tools/popup/send/efflux-dates.component"; import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; import { SendTypeComponent } from "../tools/popup/send/send-type.component"; @@ -133,7 +132,6 @@ import "../platform/popup/locales"; PrivateModeWarningComponent, RegisterComponent, SendAddEditComponent, - SendEffluxDatesComponent, SendGroupingsComponent, SendListComponent, SendTypeComponent, diff --git a/apps/browser/src/tools/popup/send/efflux-dates.component.html b/apps/browser/src/tools/popup/send/efflux-dates.component.html deleted file mode 100644 index 737fdae4aab..00000000000 --- a/apps/browser/src/tools/popup/send/efflux-dates.component.html +++ /dev/null @@ -1,217 +0,0 @@ - - -
    -
    - -
    - - -
    -
    - -
    -
    -
    -
    - - -
    - -
    -
    - -
    - - - -
    - - -
    -
    - -
    - - -
    -
    - - - -
    -
    - - - -
    - - -
    -
    - -
    - - -
    -
    - - - -
    -
    - diff --git a/apps/browser/src/tools/popup/send/efflux-dates.component.ts b/apps/browser/src/tools/popup/send/efflux-dates.component.ts deleted file mode 100644 index 3d575b41fa7..00000000000 --- a/apps/browser/src/tools/popup/send/efflux-dates.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { DatePipe } from "@angular/common"; -import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { ControlContainer, NgForm } from "@angular/forms"; - -import { EffluxDatesComponent as BaseEffluxDatesComponent } from "@bitwarden/angular/tools/send/efflux-dates.component"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -@Component({ - selector: "app-send-efflux-dates", - templateUrl: "efflux-dates.component.html", - viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], -}) -export class EffluxDatesComponent extends BaseEffluxDatesComponent { - @Input() readonly inPopout: boolean; - @Output() popOutWindow = new EventEmitter(); - - constructor( - protected i18nService: I18nService, - protected platformUtilsService: PlatformUtilsService, - protected datePipe: DatePipe - ) { - super(i18nService, platformUtilsService, datePipe); - } -} diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.html b/apps/browser/src/tools/popup/send/send-add-edit.component.html index db31ad808a5..707adaa7a55 100644 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.html +++ b/apps/browser/src/tools/popup/send/send-add-edit.component.html @@ -1,4 +1,4 @@ - +
    @@ -7,7 +7,7 @@ {{ title }}
    - @@ -42,9 +42,8 @@
    @@ -66,12 +65,9 @@ >
  • -
    +
    @@ -93,9 +89,8 @@
    @@ -105,17 +100,15 @@
    -
    +
    @@ -125,13 +118,7 @@
    - +
    @@ -144,13 +131,7 @@
    - +
    @@ -170,15 +151,140 @@
    - - + +
    +
    + +
    + + +
    +
    + +
    +
    +
    + + +
    +
    + +
    + + +
    +
    + +
    + + +
    +
    + +
    +
    +
    +
    + + +
    + +
    +
    + +
    @@ -190,8 +296,7 @@ type="number" name="MaximumAccessCount" aria-describedby="maximumAccessCountHelp" - [(ngModel)]="send.maxAccessCount" - [readonly]="disableSend" + formControlName="maxAccessCount" />
    @@ -206,10 +311,9 @@
    @@ -227,9 +331,8 @@ name="Password" aria-describedby="passwordHelp" class="monospaced" - [(ngModel)]="password" + formControlName="password" appInputVerbatim - [readonly]="disableSend" />
    @@ -264,8 +367,7 @@ name="Notes" aria-describedby="notesHelp" rows="6" - [(ngModel)]="send.notes" - [readonly]="disableSend" + formControlName="notes" >
    @@ -278,13 +380,7 @@
    - +
    @@ -293,13 +389,7 @@
    - +
    diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.ts b/apps/browser/src/tools/popup/send/send-add-edit.component.ts index 1efd950c508..2d90957de7f 100644 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.ts +++ b/apps/browser/src/tools/popup/send/send-add-edit.component.ts @@ -1,5 +1,6 @@ import { DatePipe, Location } from "@angular/common"; import { Component } from "@angular/core"; +import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; @@ -47,7 +48,8 @@ export class SendAddEditComponent extends BaseAddEditComponent { private popupUtilsService: PopupUtilsService, logService: LogService, sendApiService: SendApiService, - dialogService: DialogService + dialogService: DialogService, + formBuilder: FormBuilder ) { super( i18nService, @@ -60,7 +62,8 @@ export class SendAddEditComponent extends BaseAddEditComponent { logService, stateService, sendApiService, - dialogService + dialogService, + formBuilder ); } diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index 3ac2ca29756..d14c40854cc 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -53,7 +53,6 @@ import { ExportComponent } from "./tools/export/export.component"; import { GeneratorComponent } from "./tools/generator.component"; import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component"; import { AddEditComponent as SendAddEditComponent } from "./tools/send/add-edit.component"; -import { EffluxDatesComponent as SendEffluxDatesComponent } from "./tools/send/efflux-dates.component"; import { SendComponent } from "./tools/send/send.component"; @NgModule({ @@ -87,7 +86,6 @@ import { SendComponent } from "./tools/send/send.component"; SearchComponent, SendAddEditComponent, SendComponent, - SendEffluxDatesComponent, SetPasswordComponent, SetPinComponent, SettingsComponent, diff --git a/apps/desktop/src/app/tools/send/add-edit.component.html b/apps/desktop/src/app/tools/send/add-edit.component.html index e9e1b319adb..d9f280be598 100644 --- a/apps/desktop/src/app/tools/send/add-edit.component.html +++ b/apps/desktop/src/app/tools/send/add-edit.component.html @@ -1,4 +1,4 @@ - +
    @@ -16,14 +16,7 @@
    - +
    @@ -31,20 +24,16 @@
    -
    +
    -
    +
    {{ send.file.fileName }} ({{ send.file.sizeName }})
    -
    +
    @@ -83,13 +70,7 @@
    - +
    @@ -112,14 +93,82 @@
    - - +
    +
    +
    + + + {{ + "deletionDateDesc" | i18n + }} +
    +
    + + + {{ + "deletionDateDesc" | i18n + }} +
    +
    + + + {{ + "expirationDateDesc" | i18n + }} +
    +
    + + + {{ + "expirationDateDesc" | i18n + }} +
    +
    +
    @@ -129,8 +178,7 @@ type="number" name="maxAccessCount" aria-describedby="maxAccessCountHelp" - [(ngModel)]="send.maxAccessCount" - [readOnly]="disableSend" + formControlName="maxAccessCount" />
    @@ -154,8 +202,7 @@ name="password" aria-describedby="passwordHelp" type="{{ showPassword ? 'text' : 'password' }}" - [(ngModel)]="password" - [readOnly]="disableSend" + formControlName="password" appInputVerbatim />
    @@ -167,7 +214,6 @@ appA11yTitle="{{ 'toggleVisibility' | i18n }}" [attr.aria-pressed]="showPassword" (click)="togglePasswordVisible()" - [disabled]="disableSend" >
    @@ -206,13 +251,7 @@
    - +
    @@ -220,13 +259,7 @@
    - +
    @@ -238,17 +271,11 @@
    - +
    - +
    @@ -259,13 +286,12 @@ type="submit" class="primary btn-submit" appA11yTitle="{{ 'save' | i18n }}" - [disabled]="form.loading" *ngIf="!disableSend" > -
    diff --git a/apps/desktop/src/app/tools/send/add-edit.component.ts b/apps/desktop/src/app/tools/send/add-edit.component.ts index de5d2a601ab..98764866a54 100644 --- a/apps/desktop/src/app/tools/send/add-edit.component.ts +++ b/apps/desktop/src/app/tools/send/add-edit.component.ts @@ -1,5 +1,6 @@ import { DatePipe } from "@angular/common"; import { Component } from "@angular/core"; +import { FormBuilder } from "@angular/forms"; import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -29,7 +30,8 @@ export class AddEditComponent extends BaseAddEditComponent { policyService: PolicyService, logService: LogService, sendApiService: SendApiService, - dialogService: DialogService + dialogService: DialogService, + formBuilder: FormBuilder ) { super( i18nService, @@ -42,7 +44,8 @@ export class AddEditComponent extends BaseAddEditComponent { logService, stateService, sendApiService, - dialogService + dialogService, + formBuilder ); } @@ -50,6 +53,7 @@ export class AddEditComponent extends BaseAddEditComponent { this.password = null; const send = await this.loadSend(); this.send = await send.decrypt(); + this.updateFormValues(); this.hasPassword = this.send.password != null && this.send.password.trim() !== ""; } @@ -65,4 +69,11 @@ export class AddEditComponent extends BaseAddEditComponent { this.i18nService.t("valueCopied", this.i18nService.t("sendLink")) ); } + + async resetAndLoad() { + this.sendId = null; + this.send = null; + await this.load(); + this.updateFormValues(); + } } diff --git a/apps/desktop/src/app/tools/send/efflux-dates.component.html b/apps/desktop/src/app/tools/send/efflux-dates.component.html deleted file mode 100644 index 156dfae9ddd..00000000000 --- a/apps/desktop/src/app/tools/send/efflux-dates.component.html +++ /dev/null @@ -1,62 +0,0 @@ - -
    -
    -
    - - - {{ "deletionDateDesc" | i18n }} -
    -
    - - - {{ - "deletionDateDesc" | i18n - }} -
    -
    - - - {{ "expirationDateDesc" | i18n }} -
    -
    - - - {{ - "expirationDateDesc" | i18n - }} -
    -
    -
    -
    diff --git a/apps/desktop/src/app/tools/send/efflux-dates.component.ts b/apps/desktop/src/app/tools/send/efflux-dates.component.ts deleted file mode 100644 index 40215348d55..00000000000 --- a/apps/desktop/src/app/tools/send/efflux-dates.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DatePipe } from "@angular/common"; -import { Component, OnChanges } from "@angular/core"; -import { ControlContainer, NgForm } from "@angular/forms"; - -import { EffluxDatesComponent as BaseEffluxDatesComponent } from "@bitwarden/angular/tools/send/efflux-dates.component"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -@Component({ - selector: "app-send-efflux-dates", - templateUrl: "efflux-dates.component.html", - viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], -}) -export class EffluxDatesComponent extends BaseEffluxDatesComponent implements OnChanges { - constructor( - protected i18nService: I18nService, - protected platformUtilsService: PlatformUtilsService, - protected datePipe: DatePipe - ) { - super(i18nService, platformUtilsService, datePipe); - } - - // We reuse the same form on desktop and just swap content, so need to watch these to maintin proper values. - ngOnChanges() { - this.selectedExpirationDatePreset.setValue(0); - this.selectedDeletionDatePreset.setValue(0); - this.defaultDeletionDateTime.setValue( - this.datePipe.transform(new Date(this.initialDeletionDate), "yyyy-MM-ddTHH:mm") - ); - if (this.initialExpirationDate) { - this.defaultExpirationDateTime.setValue( - this.datePipe.transform(new Date(this.initialExpirationDate), "yyyy-MM-ddTHH:mm") - ); - } else { - this.defaultExpirationDateTime.setValue(null); - } - } -} diff --git a/apps/desktop/src/app/tools/send/send.component.ts b/apps/desktop/src/app/tools/send/send.component.ts index 7ad98ceda1e..21b759e49bf 100644 --- a/apps/desktop/src/app/tools/send/send.component.ts +++ b/apps/desktop/src/app/tools/send/send.component.ts @@ -91,12 +91,10 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro this.searchBarService.setEnabled(false); } - addSend() { + async addSend() { this.action = Action.Add; if (this.addEditComponent != null) { - this.addEditComponent.sendId = null; - this.addEditComponent.send = null; - this.addEditComponent.load(); + await this.addEditComponent.resetAndLoad(); } } diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 62702349f95..4552162cc8d 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -93,7 +93,6 @@ import { GeneratorComponent } from "../tools/generator.component"; import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-history.component"; import { AccessComponent } from "../tools/send/access.component"; import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component"; -import { EffluxDatesComponent as SendEffluxDatesComponent } from "../tools/send/efflux-dates.component"; import { ToolsComponent } from "../tools/tools.component"; import { PasswordRepromptComponent } from "../vault/components/password-reprompt.component"; import { PremiumBadgeComponent } from "../vault/components/premium-badge.component"; @@ -198,7 +197,6 @@ import { SharedModule } from "./shared.module"; SecurityKeysComponent, SelectableAvatarComponent, SendAddEditComponent, - SendEffluxDatesComponent, SetPasswordComponent, SettingsComponent, ShareComponent, @@ -302,7 +300,6 @@ import { SharedModule } from "./shared.module"; SecurityKeysComponent, SelectableAvatarComponent, SendAddEditComponent, - SendEffluxDatesComponent, SetPasswordComponent, SettingsComponent, ShareComponent, diff --git a/apps/web/src/app/tools/send/add-edit.component.html b/apps/web/src/app/tools/send/add-edit.component.html index 9cd90840b63..319d988f210 100644 --- a/apps/web/src/app/tools/send/add-edit.component.html +++ b/apps/web/src/app/tools/send/add-edit.component.html @@ -1,303 +1,276 @@ -
    -
    +
    + +
    - +
    @@ -112,7 +112,9 @@ selectedProviderType === providerType.OrganizationDuo " > -
    +
    + +
    @@ -123,7 +125,7 @@
    - +

    {{ "noTwoStepProviders" | i18n }}

    diff --git a/apps/desktop/src/auth/login/login.component.html b/apps/desktop/src/auth/login/login.component.html index 978f8df562f..3e02b49af2a 100644 --- a/apps/desktop/src/auth/login/login.component.html +++ b/apps/desktop/src/auth/login/login.component.html @@ -89,7 +89,11 @@
    - +
    - +
    diff --git a/apps/web/src/app/auth/register-form/register-form.component.html b/apps/web/src/app/auth/register-form/register-form.component.html index 9a5220c57d4..e53c963c938 100644 --- a/apps/web/src/app/auth/register-form/register-form.component.html +++ b/apps/web/src/app/auth/register-form/register-form.component.html @@ -89,7 +89,7 @@
    - +
    - +
    - +

    - +
    From 30e8a906ab7980b5fdf0867a92223fe2efb900cb Mon Sep 17 00:00:00 2001 From: Will Browning <20662079+willbrowningme@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:23:56 +0200 Subject: [PATCH 107/135] [PM-3442] Change AnonAddy to addy.io (#6027) * Update anon-addy-forwarder.ts * Update generator.component.ts --- .../tools/generator/components/generator.component.ts | 2 +- .../username/email-forwarders/anon-addy-forwarder.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/angular/src/tools/generator/components/generator.component.ts b/libs/angular/src/tools/generator/components/generator.component.ts index 37904473ad9..b0a5d13915f 100644 --- a/libs/angular/src/tools/generator/components/generator.component.ts +++ b/libs/angular/src/tools/generator/components/generator.component.ts @@ -244,7 +244,7 @@ export class GeneratorComponent implements OnInit { private async initForwardOptions() { this.forwardOptions = [ - { name: "AnonAddy", value: "anonaddy", validForSelfHosted: true }, + { name: "addy.io", value: "anonaddy", validForSelfHosted: true }, { name: "DuckDuckGo", value: "duckduckgo", validForSelfHosted: false }, { name: "Fastmail", value: "fastmail", validForSelfHosted: true }, { name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false }, diff --git a/libs/common/src/tools/generator/username/email-forwarders/anon-addy-forwarder.ts b/libs/common/src/tools/generator/username/email-forwarders/anon-addy-forwarder.ts index b20f22ecf82..2b7563b2c39 100644 --- a/libs/common/src/tools/generator/username/email-forwarders/anon-addy-forwarder.ts +++ b/libs/common/src/tools/generator/username/email-forwarders/anon-addy-forwarder.ts @@ -6,10 +6,10 @@ import { ForwarderOptions } from "./forwarder-options"; export class AnonAddyForwarder implements Forwarder { async generate(apiService: ApiService, options: ForwarderOptions): Promise { if (options.apiKey == null || options.apiKey === "") { - throw "Invalid AnonAddy API token."; + throw "Invalid addy.io API token."; } if (options.anonaddy?.domain == null || options.anonaddy.domain === "") { - throw "Invalid AnonAddy domain."; + throw "Invalid addy.io domain."; } const requestInit: RequestInit = { redirect: "manual", @@ -21,7 +21,7 @@ export class AnonAddyForwarder implements Forwarder { "X-Requested-With": "XMLHttpRequest", }), }; - const url = "https://app.anonaddy.com/api/v1/aliases"; + const url = "https://app.addy.io/api/v1/aliases"; requestInit.body = JSON.stringify({ domain: options.anonaddy.domain, description: @@ -35,8 +35,8 @@ export class AnonAddyForwarder implements Forwarder { return json?.data?.email; } if (response.status === 401) { - throw "Invalid AnonAddy API token."; + throw "Invalid addy.io API token."; } - throw "Unknown AnonAddy error occurred."; + throw "Unknown addy.io error occurred."; } } From 8de65ea7915ded00297037c6a85103390176b5ca Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 7 Sep 2023 15:33:04 -0500 Subject: [PATCH 108/135] [PM-3285] Autofill v2 Feature Branch (#5939) * [PM-3285] Autofill v2 Feature Branch * [PM-2130] - Audit, Modularize, and Refactor Core autofill.js File (#5453) * split up autofill.ts, first pass * remove modification tracking comments * lessen and localize eslint disables * additional typing and formatting * update autofill v2 with PR #5364 changes (update/i18n confirm dialogs) * update autofill v2 with PR #4155 changes (add autofill support for textarea) Co-Authored-By: Manuel * move commonly used string values to constants * ts cleanup * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Working through autofill collect method * [PM-2130] Marking Removal of documentUUID as dead code * [PM-2130] Refining the implementation of collect and moving broken out utils back into class implementation * [PM-2130] Applying small refactors to AutofillCollect * [PM-2130] Refining the implementation of getAutofillFieldLabelTag to help with readability of the method * [PM-2130] Implementing jest tests for AutofillCollect methods * [PM-2130] Refining implementation for AutofillCollect * [PM-2200] Unit tests for autofill content script utilities with slight refactors (#5544) * add unit tests for urlNotSecure * add test coverage command * add unit tests for canSeeElementToStyle * canSeeElementToStyle should not return true if `animateTheFilling` or `currentEl` is false * add tests for selectAllFromDoc and getElementByOpId * clean up getElementByOpId * address some typing issues * add tests for setValueForElementByEvent, setValueForElement, and doSimpleSetByQuery * clean up setValueForElement and setValueForElementByEvent * more typescript cleanup * add tests for doClickByOpId and touchAllPasswordFields * add tests for doFocusByOpId and doClickByQuery * misc fill cleanup * move functions between collect and fill utils and replace getElementForOPID for duplicate getElementByOpId * add tests for isKnownTag and isElementVisible * rename addProp and remove redundant focusElement in favor of doFocusElement * cleanup * fix checkNodeType * add tests for shiftForLeftLabel * clean up and rename checkNodeType, isKnownTag, and shiftForLeftLabel * add tests for getFormElements * clean up getFormElements * add tests for getElementAttrValue, getElementValue, getSelectElementOptions, getLabelTop, and queryDoc * clean up and rename queryDoc to queryDocument * misc cleanup and rename getElementAttrValue to getPropertyOrAttribute * rebase cleanup * prettier formatting * [PM-2130] Fixing linting issues * [PM-2130] Fixing linting issues * [PM-2130] Migrating implementation for collect methods and tests for those methods into AutofillCollect context * [PM-2130] Migrating getPropertyOrAttribute method from utils to AutofillCollect * [PM-2130] Continuing migration of methods from collect utils into AutofillCollect * [PM-2130] Rework of isViewable method to better handle behavior for how we identify if an element is currently within the viewport * [PM-2130] Filling out implementation of autofill-insert * [PM-2130] Refining AutofillInsert * [PM-2130] Implementing jest tests for AutofillCollect methods and breaking out visibility related logic to a separate service * [PM-2130] Fixing jest tests for AutofillCollect * [PM-2130] Fixing jest tests for AutofillInit * [PM-2130] Adjusting how the AutofillFieldVisibilityService class is used in AutofillCollect * [PM-2130] Working through AutofillInsert implementation * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Applying fix for IntersectionObserver when triggering behavior in Safari and fixing issue with how we trigger an input event shortly after filling in a field * [PM-2130] Refactoring AutofillCollect to service CollectAutofillContentService * [PM-2130] Refactoring AutofillInsert to service InsertAutofillContentService * [PM-2130] Further organization of implementation * [PM-2130] Filling out missing jest test for AutofillInit.fillForm method * [PM-2130] Migrating the last of the collect jest tests to InsertAutofillContentService * [PM-2130] Further refactoring of elements including typing information * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Organization and refactoring of methods within InsertAutofillContent * [PM-2130] Implementation of jest tests for InsertAutofillContentService * [PM-2130] Implementation of Jest Test for IntertAutofillContentService * [PM-2130] Finalizing migration of methods and jest tests from util files into Autofill serivces * [PM-2130] Cleaning up dead code comments * [PM-2130] Removing unnecessary constants * [PM-2130] Finalizing jest tests for InsertAutofillContentService * [PM-2130] Refactoring FieldVisibiltyService to DomElementVisibilityService to allow service to act in a more general manner * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Breaking out the callback method used to resolve the IntersectionObserver promise * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Applying changes required for PM-2762 to implementation, and ensuring jest tests exist to validate the behavior * [PM-2130] Removing usage of IntersectionObserver when identifying element visibility due to broken interactions with React Components * [PM-2130] Fixing issue found when attempting to capture the elementAtCenterPoint in determining file visibility * [PM-2100] Create Unit Test Suite for autofill.service.ts (#5371) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing test test for when we need to handle a password reprompt --------- Co-authored-by: Manuel Co-authored-by: Cesar Gonzalez Co-authored-by: Cesar Gonzalez * [PM-3285] Migrating Changes from PM-1407 into autofill v2 refactor implementation * [PM-2747] Add Support for Feature Flag of Autofill Version (#5695) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * split up autofill.ts, first pass * remove modification tracking comments * lessen and localize eslint disables * additional typing and formatting * update autofill v2 with PR #5364 changes (update/i18n confirm dialogs) * update autofill v2 with PR #4155 changes (add autofill support for textarea) Co-Authored-By: Manuel * move commonly used string values to constants * ts cleanup * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Working through autofill collect method * [PM-2130] Marking Removal of documentUUID as dead code * [PM-2130] Refining the implementation of collect and moving broken out utils back into class implementation * [PM-2130] Applying small refactors to AutofillCollect * [PM-2130] Refining the implementation of getAutofillFieldLabelTag to help with readability of the method * [PM-2130] Implementing jest tests for AutofillCollect methods * [PM-2130] Refining implementation for AutofillCollect * [PM-2200] Unit tests for autofill content script utilities with slight refactors (#5544) * add unit tests for urlNotSecure * add test coverage command * add unit tests for canSeeElementToStyle * canSeeElementToStyle should not return true if `animateTheFilling` or `currentEl` is false * add tests for selectAllFromDoc and getElementByOpId * clean up getElementByOpId * address some typing issues * add tests for setValueForElementByEvent, setValueForElement, and doSimpleSetByQuery * clean up setValueForElement and setValueForElementByEvent * more typescript cleanup * add tests for doClickByOpId and touchAllPasswordFields * add tests for doFocusByOpId and doClickByQuery * misc fill cleanup * move functions between collect and fill utils and replace getElementForOPID for duplicate getElementByOpId * add tests for isKnownTag and isElementVisible * rename addProp and remove redundant focusElement in favor of doFocusElement * cleanup * fix checkNodeType * add tests for shiftForLeftLabel * clean up and rename checkNodeType, isKnownTag, and shiftForLeftLabel * add tests for getFormElements * clean up getFormElements * add tests for getElementAttrValue, getElementValue, getSelectElementOptions, getLabelTop, and queryDoc * clean up and rename queryDoc to queryDocument * misc cleanup and rename getElementAttrValue to getPropertyOrAttribute * rebase cleanup * prettier formatting * [PM-2130] Fixing linting issues * [PM-2130] Fixing linting issues * [PM-2130] Migrating implementation for collect methods and tests for those methods into AutofillCollect context * [PM-2130] Migrating getPropertyOrAttribute method from utils to AutofillCollect * [PM-2130] Continuing migration of methods from collect utils into AutofillCollect * [PM-2130] Rework of isViewable method to better handle behavior for how we identify if an element is currently within the viewport * [PM-2130] Filling out implementation of autofill-insert * [PM-2130] Refining AutofillInsert * [PM-2130] Implementing jest tests for AutofillCollect methods and breaking out visibility related logic to a separate service * [PM-2130] Fixing jest tests for AutofillCollect * [PM-2130] Fixing jest tests for AutofillInit * [PM-2130] Adjusting how the AutofillFieldVisibilityService class is used in AutofillCollect * [PM-2130] Working through AutofillInsert implementation * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Applying fix for IntersectionObserver when triggering behavior in Safari and fixing issue with how we trigger an input event shortly after filling in a field * [PM-2130] Refactoring AutofillCollect to service CollectAutofillContentService * [PM-2130] Refactoring AutofillInsert to service InsertAutofillContentService * [PM-2130] Further organization of implementation * [PM-2130] Filling out missing jest test for AutofillInit.fillForm method * [PM-2130] Migrating the last of the collect jest tests to InsertAutofillContentService * [PM-2130] Further refactoring of elements including typing information * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Organization and refactoring of methods within InsertAutofillContent * [PM-2130] Implementation of jest tests for InsertAutofillContentService * [PM-2130] Implementation of Jest Test for IntertAutofillContentService * [PM-2130] Finalizing migration of methods and jest tests from util files into Autofill serivces * [PM-2130] Cleaning up dead code comments * [PM-2130] Removing unnecessary constants * [PM-2130] Finalizing jest tests for InsertAutofillContentService * [PM-2130] Refactoring FieldVisibiltyService to DomElementVisibilityService to allow service to act in a more general manner * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Breaking out the callback method used to resolve the IntersectionObserver promise * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2747] Add Support for Feature Flag of Autofill Version * [PM-2747] Adding Support for Manifest v3 within the implementation * [PM-2747] Modifying how the feature flag for autofill is named * [PM-2747] Modifying main.background.ts to load the ConfigApiService correctly * [PM-2747] Refactoring trigger of autofill scripts to be a simple immediately invoked function * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2130] Applying changes required for PM-2762 to implementation, and ensuring jest tests exist to validate the behavior * [PM-2747] Modifying how we inject the autofill scripts to ensure we are injecting into all frames within a page * [PM-2130] Removing usage of IntersectionObserver when identifying element visibility due to broken interactions with React Components * [PM-2130] Fixing issue found when attempting to capture the elementAtCenterPoint in determining file visibility * [PM-2100] Create Unit Test Suite for autofill.service.ts (#5371) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2747] Applying a fix for a race condition that can occur when loading the notification bar and autofiller script login * [PM-2747] Reverting removal of autofill npm action. Now this will force usage of autofill-v2 regardless of whether a feature flag is set or not * [PM-2747] Fixing logic error incorporated when merging in master * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing issue with autofill service unit tests * [PM-2747] Fixing issue present with notification bar merge * [PM-2130] Fixing test test for when we need to handle a password reprompt * [PM-2747] Fixing wording for webpack script * [PM-2747] Addressing stylistic changes requested from code review * [PM-2747] Addressing stylistic changes requested from code review --------- Co-authored-by: Jonathan Prusik Co-authored-by: Manuel Co-authored-by: Jonathan Prusik * [PM-3285] Applying stylistic changes suggested by code review for the feature flag implementation * [PM-3285] Adding temporary console log to validate which version is being used * [PM-3285] Removing temporary console log indicating which version of autofill the user is currently loading --------- Co-authored-by: Jonathan Prusik Co-authored-by: Manuel Co-authored-by: Jonathan Prusik --- apps/browser/package.json | 2 +- apps/browser/src/autofill/constants.ts | 13 + .../content/abstractions/autofill-init.ts | 21 + .../autofill/content/autofill-init.spec.ts | 175 + .../src/autofill/content/autofill-init.ts | 130 + .../src/autofill/content/autofiller.ts | 10 +- .../src/autofill/content/autofillv2.ts | 1399 ------ .../src/autofill/content/notification-bar.ts | 121 +- .../trigger-autofill-script-injection.spec.ts | 16 + .../trigger-autofill-script-injection.ts | 3 + apps/browser/src/autofill/globals.d.ts | 7 + .../src/autofill/jest/autofill-mocks.ts | 131 + .../src/autofill/jest/testing-utils.ts | 5 + .../src/autofill/models/autofill-field.ts | 150 +- .../autofill/models/autofill-page-details.ts | 4 - .../src/autofill/models/autofill-script.ts | 21 +- .../services/abstractions/autofill.service.ts | 22 +- .../collect-autofill-content.service.ts | 8 + .../dom-element-visibility.service.ts | 6 + .../insert-autofill-content.service.ts | 7 + .../services/autofill.service.spec.ts | 4226 +++++++++++++++++ .../src/autofill/services/autofill.service.ts | 303 +- .../collect-autofill-content.service.spec.ts | 1588 +++++++ .../collect-autofill-content.service.ts | 578 +++ .../dom-element-visibility.service.spec.ts | 409 ++ .../dom-element-visibility.service.ts | 199 + .../insert-autofill-content.service.spec.ts | 1047 ++++ .../insert-autofill-content.service.ts | 349 ++ apps/browser/src/autofill/types/index.ts | 19 + .../browser/src/background/main.background.ts | 2 + .../src/background/runtime.background.ts | 7 + apps/browser/src/manifest.json | 7 +- .../src/platform/browser/browser-api.spec.ts | 56 + .../src/platform/browser/browser-api.ts | 27 + apps/browser/test.setup.ts | 16 + apps/browser/tsconfig.spec.json | 5 +- apps/browser/webpack.config.js | 15 +- libs/common/src/enums/feature-flag.enum.ts | 1 + 38 files changed, 9490 insertions(+), 1615 deletions(-) create mode 100644 apps/browser/src/autofill/constants.ts create mode 100644 apps/browser/src/autofill/content/abstractions/autofill-init.ts create mode 100644 apps/browser/src/autofill/content/autofill-init.spec.ts create mode 100644 apps/browser/src/autofill/content/autofill-init.ts delete mode 100644 apps/browser/src/autofill/content/autofillv2.ts create mode 100644 apps/browser/src/autofill/content/trigger-autofill-script-injection.spec.ts create mode 100644 apps/browser/src/autofill/content/trigger-autofill-script-injection.ts create mode 100644 apps/browser/src/autofill/globals.d.ts create mode 100644 apps/browser/src/autofill/jest/autofill-mocks.ts create mode 100644 apps/browser/src/autofill/jest/testing-utils.ts create mode 100644 apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts create mode 100644 apps/browser/src/autofill/services/abstractions/dom-element-visibility.service.ts create mode 100644 apps/browser/src/autofill/services/abstractions/insert-autofill-content.service.ts create mode 100644 apps/browser/src/autofill/services/autofill.service.spec.ts create mode 100644 apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts create mode 100644 apps/browser/src/autofill/services/collect-autofill-content.service.ts create mode 100644 apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts create mode 100644 apps/browser/src/autofill/services/dom-element-visibility.service.ts create mode 100644 apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts create mode 100644 apps/browser/src/autofill/services/insert-autofill-content.service.ts create mode 100644 apps/browser/src/platform/browser/browser-api.spec.ts diff --git a/apps/browser/package.json b/apps/browser/package.json index 980eb3258a2..cec3a9caab1 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -6,7 +6,6 @@ "build:mv3": "cross-env MANIFEST_VERSION=3 webpack", "build:watch": "webpack --watch", "build:watch:mv3": "cross-env MANIFEST_VERSION=3 webpack --watch", - "build:watch:autofill": "cross-env AUTOFILL_VERSION=2 webpack --watch", "build:prod": "cross-env NODE_ENV=production webpack", "build:prod:watch": "cross-env NODE_ENV=production webpack --watch", "dist": "npm run build:prod && gulp dist", @@ -19,6 +18,7 @@ "dist:safari:masdev": "npm run build:prod && gulp dist:safari:masdev", "dist:safari:dmg": "npm run build:prod && gulp dist:safari:dmg", "test": "jest", + "test:coverage": "jest --coverage --coverageDirectory=coverage", "test:watch": "jest --watch", "test:watch:all": "jest --watchAll" } diff --git a/apps/browser/src/autofill/constants.ts b/apps/browser/src/autofill/constants.ts new file mode 100644 index 00000000000..7f3637180b0 --- /dev/null +++ b/apps/browser/src/autofill/constants.ts @@ -0,0 +1,13 @@ +export const TYPE_CHECK = { + FUNCTION: "function", + NUMBER: "number", + STRING: "string", +} as const; + +export const EVENTS = { + CHANGE: "change", + INPUT: "input", + KEYDOWN: "keydown", + KEYPRESS: "keypress", + KEYUP: "keyup", +} as const; diff --git a/apps/browser/src/autofill/content/abstractions/autofill-init.ts b/apps/browser/src/autofill/content/abstractions/autofill-init.ts new file mode 100644 index 00000000000..706c6da4ee1 --- /dev/null +++ b/apps/browser/src/autofill/content/abstractions/autofill-init.ts @@ -0,0 +1,21 @@ +import AutofillScript from "../../models/autofill-script"; + +type AutofillExtensionMessage = { + command: string; + tab?: chrome.tabs.Tab; + sender?: string; + fillScript?: AutofillScript; +}; + +type AutofillExtensionMessageHandlers = { + [key: string]: CallableFunction; + collectPageDetails: (message: { message: AutofillExtensionMessage }) => void; + collectPageDetailsImmediately: (message: { message: AutofillExtensionMessage }) => void; + fillForm: (message: { message: AutofillExtensionMessage }) => void; +}; + +interface AutofillInit { + init(): void; +} + +export { AutofillExtensionMessage, AutofillExtensionMessageHandlers, AutofillInit }; diff --git a/apps/browser/src/autofill/content/autofill-init.spec.ts b/apps/browser/src/autofill/content/autofill-init.spec.ts new file mode 100644 index 00000000000..447fe31a8a3 --- /dev/null +++ b/apps/browser/src/autofill/content/autofill-init.spec.ts @@ -0,0 +1,175 @@ +import { mock } from "jest-mock-extended"; + +import AutofillPageDetails from "../models/autofill-page-details"; +import AutofillScript from "../models/autofill-script"; + +import { AutofillExtensionMessage } from "./abstractions/autofill-init"; + +describe("AutofillInit", () => { + let bitwardenAutofillInit: any; + + beforeEach(() => { + require("../content/autofill-init"); + bitwardenAutofillInit = window.bitwardenAutofillInit; + }); + + afterEach(() => { + jest.resetModules(); + jest.clearAllMocks(); + }); + + describe("init", () => { + it("sets up the extension message listeners", () => { + jest.spyOn(bitwardenAutofillInit, "setupExtensionMessageListeners"); + + bitwardenAutofillInit.init(); + + expect(bitwardenAutofillInit.setupExtensionMessageListeners).toHaveBeenCalled(); + }); + }); + + describe("collectPageDetails", () => { + let extensionMessage: AutofillExtensionMessage; + let pageDetails: AutofillPageDetails; + + beforeEach(() => { + extensionMessage = { + command: "collectPageDetails", + tab: mock(), + sender: "sender", + }; + pageDetails = { + title: "title", + url: "http://example.com", + documentUrl: "documentUrl", + forms: {}, + fields: [], + collectedTimestamp: 0, + }; + jest + .spyOn(bitwardenAutofillInit.collectAutofillContentService, "getPageDetails") + .mockReturnValue(pageDetails); + }); + + it("returns collected page details for autofill if set to send the details in the response", async () => { + const response = await bitwardenAutofillInit["collectPageDetails"](extensionMessage, true); + + expect(bitwardenAutofillInit.collectAutofillContentService.getPageDetails).toHaveBeenCalled(); + expect(response).toEqual(pageDetails); + }); + + it("sends the collected page details for autofill using a background script message", async () => { + jest.spyOn(chrome.runtime, "sendMessage"); + + await bitwardenAutofillInit["collectPageDetails"](extensionMessage); + + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + command: "collectPageDetailsResponse", + tab: extensionMessage.tab, + details: pageDetails, + sender: extensionMessage.sender, + }); + }); + }); + + describe("fillForm", () => { + it("will call the InsertAutofillContentService to fill the form", () => { + const fillScript = mock(); + jest + .spyOn(bitwardenAutofillInit.insertAutofillContentService, "fillForm") + .mockImplementation(); + + bitwardenAutofillInit.fillForm(fillScript); + + expect(bitwardenAutofillInit.insertAutofillContentService.fillForm).toHaveBeenCalledWith( + fillScript + ); + }); + }); + + describe("setupExtensionMessageListeners", () => { + it("sets up a chrome runtime on message listener", () => { + jest.spyOn(chrome.runtime.onMessage, "addListener"); + + bitwardenAutofillInit["setupExtensionMessageListeners"](); + + expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledWith( + bitwardenAutofillInit["handleExtensionMessage"] + ); + }); + }); + + describe("handleExtensionMessage", () => { + let message: AutofillExtensionMessage; + let sender: chrome.runtime.MessageSender; + const sendResponse = jest.fn(); + + beforeEach(() => { + message = { + command: "collectPageDetails", + tab: mock(), + sender: "sender", + }; + sender = mock(); + }); + + it("returns a false value if a extension message handler is not found with the given message command", () => { + message.command = "unknownCommand"; + + const response = bitwardenAutofillInit["handleExtensionMessage"]( + message, + sender, + sendResponse + ); + + expect(response).toBe(false); + }); + + it("returns a false value if the message handler does not return a response", async () => { + const response1 = await bitwardenAutofillInit["handleExtensionMessage"]( + message, + sender, + sendResponse + ); + await Promise.resolve(response1); + + expect(response1).not.toBe(false); + + message.command = "fillForm"; + message.fillScript = mock(); + + const response2 = await bitwardenAutofillInit["handleExtensionMessage"]( + message, + sender, + sendResponse + ); + + expect(response2).toBe(false); + }); + + it("returns a true value and calls sendResponse if the message handler returns a response", async () => { + message.command = "collectPageDetailsImmediately"; + const pageDetails: AutofillPageDetails = { + title: "title", + url: "http://example.com", + documentUrl: "documentUrl", + forms: {}, + fields: [], + collectedTimestamp: 0, + }; + jest + .spyOn(bitwardenAutofillInit.collectAutofillContentService, "getPageDetails") + .mockReturnValue(pageDetails); + + const response = await bitwardenAutofillInit["handleExtensionMessage"]( + message, + sender, + sendResponse + ); + await Promise.resolve(response); + + expect(response).toBe(true); + expect(sendResponse).toHaveBeenCalledWith(pageDetails); + }); + }); +}); diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts new file mode 100644 index 00000000000..8b441ae0e20 --- /dev/null +++ b/apps/browser/src/autofill/content/autofill-init.ts @@ -0,0 +1,130 @@ +import AutofillPageDetails from "../models/autofill-page-details"; +import AutofillScript from "../models/autofill-script"; +import CollectAutofillContentService from "../services/collect-autofill-content.service"; +import DomElementVisibilityService from "../services/dom-element-visibility.service"; +import InsertAutofillContentService from "../services/insert-autofill-content.service"; + +import { + AutofillExtensionMessage, + AutofillExtensionMessageHandlers, + AutofillInit as AutofillInitInterface, +} from "./abstractions/autofill-init"; + +class AutofillInit implements AutofillInitInterface { + private readonly domElementVisibilityService: DomElementVisibilityService; + private readonly collectAutofillContentService: CollectAutofillContentService; + private readonly insertAutofillContentService: InsertAutofillContentService; + private readonly extensionMessageHandlers: AutofillExtensionMessageHandlers = { + collectPageDetails: ({ message }) => this.collectPageDetails(message), + collectPageDetailsImmediately: ({ message }) => this.collectPageDetails(message, true), + fillForm: ({ message }) => this.fillForm(message.fillScript), + }; + + /** + * AutofillInit constructor. Initializes the DomElementVisibilityService, + * CollectAutofillContentService and InsertAutofillContentService classes. + */ + constructor() { + this.domElementVisibilityService = new DomElementVisibilityService(); + this.collectAutofillContentService = new CollectAutofillContentService( + this.domElementVisibilityService + ); + this.insertAutofillContentService = new InsertAutofillContentService( + this.domElementVisibilityService, + this.collectAutofillContentService + ); + } + + /** + * Initializes the autofill content script, setting up + * the extension message listeners. This method should + * be called once when the content script is loaded. + * @public + */ + init() { + this.setupExtensionMessageListeners(); + } + + /** + * Collects the page details and sends them to the + * extension background script. If the `sendDetailsInResponse` + * parameter is set to true, the page details will be + * returned to facilitate sending the details in the + * response to the extension message. + * @param {AutofillExtensionMessage} message + * @param {boolean} sendDetailsInResponse + * @returns {AutofillPageDetails | void} + * @private + */ + private async collectPageDetails( + message: AutofillExtensionMessage, + sendDetailsInResponse = false + ): Promise { + const pageDetails: AutofillPageDetails = + await this.collectAutofillContentService.getPageDetails(); + if (sendDetailsInResponse) { + return pageDetails; + } + + chrome.runtime.sendMessage({ + command: "collectPageDetailsResponse", + tab: message.tab, + details: pageDetails, + sender: message.sender, + }); + } + + /** + * Fills the form with the given fill script. + * @param {AutofillScript} fillScript + * @private + */ + private fillForm(fillScript: AutofillScript) { + this.insertAutofillContentService.fillForm(fillScript); + } + + /** + * Sets up the extension message listeners + * for the content script. + * @private + */ + private setupExtensionMessageListeners() { + chrome.runtime.onMessage.addListener(this.handleExtensionMessage); + } + + /** + * Handles the extension messages + * sent to the content script. + * @param {AutofillExtensionMessage} message + * @param {chrome.runtime.MessageSender} sender + * @param {(response?: any) => void} sendResponse + * @returns {boolean} + * @private + */ + private handleExtensionMessage = ( + message: AutofillExtensionMessage, + sender: chrome.runtime.MessageSender, + sendResponse: (response?: any) => void + ): boolean => { + const command: string = message.command; + const handler: CallableFunction | undefined = this.extensionMessageHandlers[command]; + if (!handler) { + return false; + } + + const messageResponse = handler({ message, sender }); + if (!messageResponse) { + return false; + } + + Promise.resolve(messageResponse).then((response) => sendResponse(response)); + return true; + }; +} + +(function () { + if (!window.bitwardenAutofillInit) { + window.bitwardenAutofillInit = new AutofillInit(); + window.bitwardenAutofillInit.init(); + } +})(); diff --git a/apps/browser/src/autofill/content/autofiller.ts b/apps/browser/src/autofill/content/autofiller.ts index 7fe9e5514a8..7f58e72c7d3 100644 --- a/apps/browser/src/autofill/content/autofiller.ts +++ b/apps/browser/src/autofill/content/autofiller.ts @@ -1,4 +1,10 @@ -document.addEventListener("DOMContentLoaded", (event) => { +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", loadAutofiller); +} else { + loadAutofiller(); +} + +function loadAutofiller() { let pageHref: string = null; let filledThisHref = false; let delayFillTimeout: number; @@ -49,4 +55,4 @@ document.addEventListener("DOMContentLoaded", (event) => { chrome.runtime.sendMessage(msg); } } -}); +} diff --git a/apps/browser/src/autofill/content/autofillv2.ts b/apps/browser/src/autofill/content/autofillv2.ts deleted file mode 100644 index 65813b3afe6..00000000000 --- a/apps/browser/src/autofill/content/autofillv2.ts +++ /dev/null @@ -1,1399 +0,0 @@ -/* eslint-disable no-var, no-console, no-prototype-builtins */ -// These eslint rules are disabled because the original JS was not written with them in mind and we don't want to fix -// them all now - -/* - 1Password Extension - - Lovingly handcrafted by Dave Teare, Michael Fey, Rad Azzouz, and Roustem Karimov. - Copyright (c) 2014 AgileBits. All rights reserved. - - ================================================================================ - - Copyright (c) 2014 AgileBits Inc. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ - -/* - MODIFICATIONS FROM ORIGINAL - - 1. Populate isFirefox - 2. Remove isChrome and isSafari since they are not used. - 3. Unminify and format to meet Mozilla review requirements. - 4. Remove unnecessary input types from getFormElements query selector and limit number of elements returned. - 5. Remove fakeTested prop. - 6. Rename com.agilebits.* stuff to com.bitwarden.* - 7. Remove "some useful globals" on window - 8. Add ability to autofill span[data-bwautofill] elements - 9. Add new handler, for new command that responds with page details in response callback - 10. Handle sandbox iframe and sandbox rule in CSP - 11. Work on array of saved urls instead of just one to determine if we should autofill non-https sites - 12. Remove setting of attribute com.browser.browser.userEdited on user-inputs - 13. Handle null value URLs in urlNotSecure - 14. Convert to Typescript, add typings and remove dead code (not marked with START/END MODIFICATION) - */ -import AutofillForm from "../models/autofill-form"; -import AutofillPageDetails from "../models/autofill-page-details"; -import AutofillScript, { - AutofillScriptOptions, - FillScript, - FillScriptOp, -} from "../models/autofill-script"; - -/** - * The Document with additional custom properties added by this script - */ -type AutofillDocument = Document & { - elementsByOPID: Record; - elementForOPID: (opId: string) => Element; -}; - -/** - * A HTMLElement (usually a form element) with additional custom properties added by this script - */ -type ElementWithOpId = T & { - opid: string; -}; - -/** - * This script's definition of a Form Element (only a subset of HTML form elements) - * This is defined by getFormElements - */ -type FormElement = HTMLInputElement | HTMLSelectElement | HTMLSpanElement; - -/** - * A Form Element that we can set a value on (fill) - */ -type FillableControl = HTMLInputElement | HTMLSelectElement; - -function collect(document: Document) { - // START MODIFICATION - var isFirefox = - navigator.userAgent.indexOf("Firefox") !== -1 || navigator.userAgent.indexOf("Gecko/") !== -1; - // END MODIFICATION - - (document as AutofillDocument).elementsByOPID = {}; - - function getPageDetails(theDoc: Document, oneShotId: string) { - // start helpers - - /** - * For a given element `el`, returns the value of the attribute `attrName`. - * @param {HTMLElement} el - * @param {string} attrName - * @returns {string} The value of the attribute - */ - function getElementAttrValue(el: any, attrName: string) { - var attrVal = el[attrName]; - if ("string" == typeof attrVal) { - return attrVal; - } - attrVal = el.getAttribute(attrName); - return "string" == typeof attrVal ? attrVal : null; - } - - /** - * Returns the value of the given element. - * @param {HTMLElement} el - * @returns {any} Value of the element - */ - function getElementValue(el: any) { - switch (toLowerString(el.type)) { - case "checkbox": - return el.checked ? "✓" : ""; - - case "hidden": - el = el.value; - if (!el || "number" != typeof el.length) { - return ""; - } - 254 < el.length && (el = el.substr(0, 254) + "...SNIPPED"); - return el; - - default: - // START MODIFICATION - if (!el.type && el.tagName.toLowerCase() === "span") { - return el.innerText; - } - // END MODIFICATION - return el.value; - } - } - - /** - * If `el` is a `` elements, an array of the element's option `text` values - */ - selectInfo: any; - /** - * The `maxLength` attribute for the field - */ - maxLength: number; + htmlClass: string | null; + + tabindex: string | null; + + title: string | null; /** * The `tagName` for the field */ - tagName: string; - [key: string]: any; + tagName?: string | null; + /** + * The concatenated `innerText` or `textContent` of all the elements that are to the "left" of the field in the DOM + */ + "label-left"?: string; + /** + * The concatenated `innerText` or `textContent` of all the elements that are to the "right" of the field in the DOM + */ + "label-right"?: string; + /** + * For fields in a data table, the contents of the table row immediately above the field + */ + "label-top"?: string; + /** + * The concatenated `innerText` or `textContent` of all elements that are HTML labels for the field + */ + "label-tag"?: string; + /** + * The `aria-label` attribute for the field + */ + "label-aria"?: string | null; + + "label-data"?: string | null; + + "aria-hidden"?: boolean; + + "aria-disabled"?: boolean; + + "aria-haspopup"?: boolean; + + "data-stripe"?: string | null; + /** + * The HTML `placeholder` attribute for the field + */ + placeholder?: string | null; + /** + * The HTML `type` attribute for the field + */ + type?: string; + /** + * The HTML `value` for the field + */ + value?: string; + /** + * The `disabled` status of the field + */ + disabled?: boolean; + /** + * The `readonly` status of the field + */ + readonly?: boolean; + /** + * The `opid` attribute value of the form that contains the field + */ + form?: string; + /** + * The `x-autocompletetype`, `autocompletetype`, or `autocomplete` attribute for the field + */ + autoCompleteType?: string | null; + /** + * For ` + + +
    +`; + +describe("CollectAutofillContentService", () => { + const domElementVisibilityService = new DomElementVisibilityService(); + let collectAutofillContentService: CollectAutofillContentService; + + beforeEach(() => { + document.body.innerHTML = mockLoginForm; + collectAutofillContentService = new CollectAutofillContentService(domElementVisibilityService); + }); + + afterEach(() => { + jest.clearAllMocks(); + document.body.innerHTML = ""; + }); + + describe("getPageDetails", () => { + it("returns an object containing information about the curren page as well as autofill data for the forms and fields of the page", async () => { + const documentTitle = "Test Page"; + const formId = "validFormId"; + const formAction = "https://example.com/"; + const formMethod = "post"; + const formName = "validFormName"; + const usernameFieldId = "usernameField"; + const usernameFieldName = "username"; + const usernameFieldLabel = "User Name"; + const passwordFieldId = "passwordField"; + const passwordFieldName = "password"; + const passwordFieldLabel = "Password"; + document.title = documentTitle; + document.body.innerHTML = ` +
    + + + + +
    + `; + jest.spyOn(collectAutofillContentService as any, "buildAutofillFormsData"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldsData"); + jest + .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") + .mockResolvedValue(true); + + const pageDetails = await collectAutofillContentService.getPageDetails(); + + expect(collectAutofillContentService["buildAutofillFormsData"]).toHaveBeenCalled(); + expect(collectAutofillContentService["buildAutofillFieldsData"]).toHaveBeenCalled(); + expect(pageDetails).toStrictEqual({ + title: documentTitle, + url: window.location.href, + documentUrl: document.location.href, + forms: { + __form__0: { + opid: "__form__0", + htmlAction: formAction, + htmlName: formName, + htmlID: formId, + htmlMethod: formMethod, + }, + }, + fields: [ + { + opid: "__0", + elementNumber: 0, + maxLength: 999, + viewable: true, + htmlID: usernameFieldId, + htmlName: usernameFieldName, + htmlClass: null, + tabindex: null, + title: "", + tagName: "input", + "label-tag": usernameFieldLabel, + "label-data": null, + "label-aria": null, + "label-top": null, + "label-right": passwordFieldLabel, + "label-left": usernameFieldLabel, + placeholder: "", + rel: null, + type: "text", + value: "", + checked: false, + autoCompleteType: "", + disabled: false, + readonly: false, + selectInfo: null, + form: "__form__0", + "aria-hidden": false, + "aria-disabled": false, + "aria-haspopup": false, + "data-stripe": null, + }, + { + opid: "__1", + elementNumber: 1, + maxLength: 999, + viewable: true, + htmlID: passwordFieldId, + htmlName: passwordFieldName, + htmlClass: null, + tabindex: null, + title: "", + tagName: "input", + "label-tag": passwordFieldLabel, + "label-data": null, + "label-aria": null, + "label-top": null, + "label-right": "", + "label-left": passwordFieldLabel, + placeholder: "", + rel: null, + type: "password", + value: "", + checked: false, + autoCompleteType: "", + disabled: false, + readonly: false, + selectInfo: null, + form: "__form__0", + "aria-hidden": false, + "aria-disabled": false, + "aria-haspopup": false, + "data-stripe": null, + }, + ], + collectedTimestamp: expect.any(Number), + }); + }); + }); + + describe("getAutofillFieldElementByOpid", () => { + it("returns the element with the opid property value matching the passed value", () => { + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + const passwordInput = document.querySelector( + 'input[type="password"]' + ) as FormElementWithAttribute; + textInput.opid = "__0"; + passwordInput.opid = "__1"; + + const textInputWithOpid = collectAutofillContentService.getAutofillFieldElementByOpid("__0"); + const passwordInputWithOpid = + collectAutofillContentService.getAutofillFieldElementByOpid("__1"); + + expect(textInputWithOpid).toEqual(textInput); + expect(textInputWithOpid).not.toEqual(passwordInput); + expect(passwordInputWithOpid).toEqual(passwordInput); + }); + + it("returns the first of the element with an `opid` value matching the passed value and emits a console warning if multiple fields contain the same `opid`", () => { + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + const passwordInput = document.querySelector( + 'input[type="password"]' + ) as FormElementWithAttribute; + jest.spyOn(console, "warn").mockImplementationOnce(jest.fn()); + textInput.opid = "__1"; + passwordInput.opid = "__1"; + + const elementWithOpid0 = collectAutofillContentService.getAutofillFieldElementByOpid("__0"); + const elementWithOpid1 = collectAutofillContentService.getAutofillFieldElementByOpid("__1"); + + expect(elementWithOpid0).toEqual(textInput); + expect(elementWithOpid1).toEqual(textInput); + expect(elementWithOpid1).not.toEqual(passwordInput); + // eslint-disable-next-line no-console + expect(console.warn).toHaveBeenCalledWith("More than one element found with opid __1"); + }); + + it("returns the element at the index position (parsed from passed opid) of all AutofillField elements when the passed opid value cannot be found", () => { + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + const passwordInput = document.querySelector( + 'input[type="password"]' + ) as FormElementWithAttribute; + textInput.opid = undefined; + passwordInput.opid = "__1"; + + const elementWithOpid0 = collectAutofillContentService.getAutofillFieldElementByOpid("__0"); + const elementWithOpid2 = collectAutofillContentService.getAutofillFieldElementByOpid("__2"); + + expect(textInput.opid).toBeUndefined(); + expect(elementWithOpid0).toEqual(textInput); + expect(elementWithOpid0).not.toEqual(passwordInput); + expect(elementWithOpid2).toBeNull(); + }); + + it("returns null if no element can be found", () => { + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + textInput.opid = "__0"; + + const foundElementWithOpid = + collectAutofillContentService.getAutofillFieldElementByOpid("__999"); + + expect(foundElementWithOpid).toBeNull(); + }); + }); + + describe("buildAutofillFormsData", () => { + it("returns an object of AutofillForm objects with the form id as a key", () => { + const documentTitle = "Test Page"; + const formId1 = "validFormId"; + const formAction1 = "https://example.com/"; + const formMethod1 = "post"; + const formName1 = "validFormName"; + const formId2 = "validFormId2"; + const formAction2 = "https://example2.com/"; + const formMethod2 = "get"; + const formName2 = "validFormName2"; + document.title = documentTitle; + document.body.innerHTML = ` +
    + + + + +
    +
    + + +
    + `; + + const autofillFormsData = collectAutofillContentService["buildAutofillFormsData"](); + + expect(autofillFormsData).toStrictEqual({ + __form__0: { + opid: "__form__0", + htmlAction: formAction1, + htmlName: formName1, + htmlID: formId1, + htmlMethod: formMethod1, + }, + __form__1: { + opid: "__form__1", + htmlAction: formAction2, + htmlName: formName2, + htmlID: formId2, + htmlMethod: formMethod2, + }, + }); + }); + }); + + describe("buildAutofillFieldsData", () => { + it("returns a promise containing an array of AutofillField objects", async () => { + jest.spyOn(collectAutofillContentService as any, "getAutofillFieldElements"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem"); + jest + .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") + .mockResolvedValue(true); + + const autofillFieldsPromise = collectAutofillContentService["buildAutofillFieldsData"](); + const autofillFieldsData = await Promise.resolve(autofillFieldsPromise); + + expect(collectAutofillContentService["getAutofillFieldElements"]).toHaveBeenCalledWith(50); + expect(collectAutofillContentService["buildAutofillFieldItem"]).toHaveBeenCalledTimes(2); + expect(autofillFieldsPromise).toBeInstanceOf(Promise); + expect(autofillFieldsData).toStrictEqual([ + { + "aria-disabled": false, + "aria-haspopup": false, + "aria-hidden": false, + autoCompleteType: "", + checked: false, + "data-stripe": null, + disabled: false, + elementNumber: 0, + form: null, + htmlClass: null, + htmlID: "username", + htmlName: "", + "label-aria": null, + "label-data": null, + "label-left": "", + "label-right": "", + "label-tag": "", + "label-top": null, + maxLength: 999, + opid: "__0", + placeholder: "", + readonly: false, + rel: null, + selectInfo: null, + tabindex: null, + tagName: "input", + title: "", + type: "text", + value: "", + viewable: true, + }, + { + "aria-disabled": false, + "aria-haspopup": false, + "aria-hidden": false, + autoCompleteType: "", + checked: false, + "data-stripe": null, + disabled: false, + elementNumber: 1, + form: null, + htmlClass: null, + htmlID: "", + htmlName: "", + "label-aria": null, + "label-data": null, + "label-left": "", + "label-right": "", + "label-tag": "", + "label-top": null, + maxLength: 999, + opid: "__1", + placeholder: "", + readonly: false, + rel: null, + selectInfo: null, + tabindex: null, + tagName: "input", + title: "", + type: "password", + value: "", + viewable: true, + }, + ]); + }); + }); + + describe("getAutofillFieldElements", () => { + it("returns all form elements from the targeted document if no limit is set", () => { + document.body.innerHTML = ` +
    +
    + + + + + + + + + Span Element +
    +
    + `; + const usernameInput = document.getElementById("username"); + const passwordInput = document.querySelector('input[type="password"]'); + const commentsTextarea = document.getElementById("comments"); + const selectElement = document.getElementById("select"); + const spanElement = document.querySelector('span[data-bwautofill="true"]'); + jest.spyOn(document, "querySelectorAll"); + jest.spyOn(collectAutofillContentService as any, "getPropertyOrAttribute"); + + const formElements: FormFieldElement[] = + collectAutofillContentService["getAutofillFieldElements"](); + + expect(document.querySelectorAll).toHaveBeenCalledWith( + 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]):not([type="file"]):not([data-bwignore]), textarea:not([data-bwignore]), select:not([data-bwignore]), span[data-bwautofill]' + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).not.toHaveBeenCalled(); + expect(formElements).toEqual([ + usernameInput, + passwordInput, + commentsTextarea, + selectElement, + spanElement, + ]); + }); + + it("returns up to 2 (passed as `limit`) form elements from the targeted document with more than 2 form elements", () => { + document.body.innerHTML = ` +
    + included span + + ignored span + + + + + another included span +
    + `; + const spanElement = document.querySelector("span[data-bwautofill='true']"); + const textAreaInput = document.querySelector("textarea"); + jest.spyOn(collectAutofillContentService as any, "getPropertyOrAttribute"); + + const formElements: FormFieldElement[] = + collectAutofillContentService["getAutofillFieldElements"](2); + + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 1, + spanElement, + "type" + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 2, + textAreaInput, + "type" + ); + expect(formElements).toEqual([spanElement, textAreaInput]); + }); + + it("returns form elements from the targeted document, ignoring input types `hidden`, `submit`, `reset`, `button`, `image`, `file`, and inputs tagged with `data-bwignore`, while giving lower order priority to `checkbox` and `radio` inputs if the returned list is truncated by `limit", () => { + document.body.innerHTML = ` +
    +
    + Select an option: +
    + + +
    +
    + + +
    +
    + + +
    +
    + included span + + ignored span + + + + + + + + + + + + + + another included span +
    + `; + const inputRadioA = document.querySelector('input[type="radio"][value="option-a"]'); + const inputRadioB = document.querySelector('input[type="radio"][value="option-b"]'); + const inputRadioC = document.querySelector('input[type="radio"][value="option-c"]'); + const firstSpan = document.getElementById("first-span"); + const textAreaInput = document.querySelector("textarea"); + const checkboxInput = document.querySelector('input[type="checkbox"]'); + const selectElement = document.querySelector("select"); + const usernameInput = document.getElementById("username"); + const passwordInput = document.querySelector('input[type="password"]'); + const secondSpan = document.getElementById("second-span"); + + const formElements: FormFieldElement[] = + collectAutofillContentService["getAutofillFieldElements"](); + + expect(formElements).toEqual([ + inputRadioA, + inputRadioB, + inputRadioC, + firstSpan, + textAreaInput, + checkboxInput, + selectElement, + usernameInput, + passwordInput, + secondSpan, + ]); + }); + + it("returns form elements from the targeted document while giving lower order priority to `checkbox` and `radio` inputs if the returned list is truncated by `limit`", () => { + document.body.innerHTML = ` +
    + + + + ignored span +
    + Select an option: +
    + + +
    +
    + + +
    +
    + + +
    +
    + + + + + another included span +
    + `; + const textAreaInput = document.querySelector("textarea"); + const selectElement = document.querySelector("select"); + const usernameInput = document.getElementById("username"); + const passwordInput = document.querySelector('input[type="password"]'); + const includedSpan = document.querySelector('span[data-bwautofill="true"]'); + const checkboxInput = document.querySelector('input[type="checkbox"]'); + const inputRadioA = document.querySelector('input[type="radio"][value="option-a"]'); + const inputRadioB = document.querySelector('input[type="radio"][value="option-b"]'); + + const truncatedFormElements: FormFieldElement[] = + collectAutofillContentService["getAutofillFieldElements"](8); + + expect(truncatedFormElements).toEqual([ + textAreaInput, + selectElement, + usernameInput, + passwordInput, + includedSpan, + checkboxInput, + inputRadioA, + inputRadioB, + ]); + }); + }); + + describe("buildAutofillFieldItem", () => { + it("returns the AutofillField base data values without the field labels or input values if the passed element is a span element", async () => { + const index = 0; + const spanElementId = "span-element"; + const spanElementClasses = "span element classes"; + const spanElementTabIndex = 0; + const spanElementTitle = "Span Element Title"; + document.body.innerHTML = ` + Span Element + `; + const spanElement = document.getElementById( + spanElementId + ) as ElementWithOpId; + jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); + jest + .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") + .mockResolvedValue(true); + jest.spyOn(collectAutofillContentService as any, "getPropertyOrAttribute"); + jest.spyOn(collectAutofillContentService as any, "getElementValue"); + + const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( + spanElement, + index + ); + + expect(collectAutofillContentService["getAutofillFieldMaxLength"]).toHaveBeenCalledWith( + spanElement + ); + expect( + collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable + ).toHaveBeenCalledWith(spanElement); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 1, + spanElement, + "id" + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 2, + spanElement, + "name" + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 3, + spanElement, + "class" + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 4, + spanElement, + "tabindex" + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 5, + spanElement, + "title" + ); + expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( + 6, + spanElement, + "tagName" + ); + expect(collectAutofillContentService["getElementValue"]).not.toHaveBeenCalled(); + expect(autofillFieldItem).toEqual({ + elementNumber: index, + htmlClass: spanElementClasses, + htmlID: spanElementId, + htmlName: null, + maxLength: null, + opid: `__${index}`, + tabindex: String(spanElementTabIndex), + tagName: spanElement.tagName.toLowerCase(), + title: spanElementTitle, + viewable: true, + }); + }); + + it("returns the AutofillField base data, label data, and input element data", async () => { + const index = 0; + const usernameField = { + labelText: "Username", + id: "username-id", + classes: "username input classes", + name: "username", + type: "text", + maxLength: 42, + tabIndex: 0, + title: "Username Input Title", + autocomplete: "username-autocomplete", + dataLabel: "username-data-label", + ariaLabel: "username-aria-label", + placeholder: "username-placeholder", + rel: "username-rel", + value: "username-value", + dataStripe: "data-stripe", + }; + document.body.innerHTML = ` +
    + + +
    + `; + const formElement = document.querySelector("form"); + formElement.opid = "form-opid"; + const usernameInput = document.getElementById( + usernameField.id + ) as ElementWithOpId; + jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); + jest + .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") + .mockResolvedValue(true); + jest.spyOn(collectAutofillContentService as any, "getPropertyOrAttribute"); + jest.spyOn(collectAutofillContentService as any, "getElementValue"); + + const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( + usernameInput, + index + ); + + expect(autofillFieldItem).toEqual({ + "aria-disabled": false, + "aria-haspopup": false, + "aria-hidden": false, + autoCompleteType: usernameField.autocomplete, + checked: false, + "data-stripe": usernameField.dataStripe, + disabled: false, + elementNumber: index, + form: formElement.opid, + htmlClass: usernameField.classes, + htmlID: usernameField.id, + htmlName: usernameField.name, + "label-aria": usernameField.ariaLabel, + "label-data": usernameField.dataLabel, + "label-left": usernameField.labelText, + "label-right": "", + "label-tag": usernameField.labelText, + "label-top": null, + maxLength: usernameField.maxLength, + opid: `__${index}`, + placeholder: usernameField.placeholder, + readonly: false, + rel: usernameField.rel, + selectInfo: null, + tabindex: String(usernameField.tabIndex), + tagName: usernameInput.tagName.toLowerCase(), + title: usernameField.title, + type: usernameField.type, + value: usernameField.value, + viewable: true, + }); + }); + + it("returns the AutofillField base data and input element data, but not the label data if the input element is of type `hidden`", async () => { + const index = 0; + const hiddenField = { + labelText: "Hidden Field", + id: "hidden-id", + classes: "hidden input classes", + name: "hidden", + type: "hidden", + maxLength: 42, + tabIndex: 0, + title: "Hidden Input Title", + autocomplete: "off", + rel: "hidden-rel", + value: "hidden-value", + dataStripe: "data-stripe", + }; + document.body.innerHTML = ` +
    + + +
    + `; + const formElement = document.querySelector("form"); + formElement.opid = "form-opid"; + const hiddenInput = document.getElementById( + hiddenField.id + ) as ElementWithOpId; + jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); + jest + .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") + .mockResolvedValue(true); + jest.spyOn(collectAutofillContentService as any, "getPropertyOrAttribute"); + jest.spyOn(collectAutofillContentService as any, "getElementValue"); + + const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( + hiddenInput, + index + ); + + expect(autofillFieldItem).toEqual({ + "aria-disabled": false, + "aria-haspopup": false, + "aria-hidden": false, + autoCompleteType: null, + checked: false, + "data-stripe": hiddenField.dataStripe, + disabled: false, + elementNumber: index, + form: formElement.opid, + htmlClass: hiddenField.classes, + htmlID: hiddenField.id, + htmlName: hiddenField.name, + maxLength: hiddenField.maxLength, + opid: `__${index}`, + readonly: false, + rel: hiddenField.rel, + selectInfo: null, + tabindex: String(hiddenField.tabIndex), + tagName: hiddenInput.tagName.toLowerCase(), + title: hiddenField.title, + type: hiddenField.type, + value: hiddenField.value, + viewable: true, + }); + }); + }); + + describe("createAutofillFieldLabelTag", () => { + beforeEach(() => { + jest.spyOn(collectAutofillContentService as any, "createLabelElementsTag"); + jest.spyOn(document, "querySelectorAll"); + }); + + it("returns the label tag early if the passed element contains any labels", () => { + document.body.innerHTML = ` + + + + `; + const element = document.querySelector("#username-id") as FillableFormFieldElement; + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( + new Set(element.labels) + ); + expect(document.querySelectorAll).not.toHaveBeenCalled(); + expect(labelTag).toEqual("Username"); + }); + + it("queries all labels associated with the element's id", () => { + document.body.innerHTML = ` + + + `; + const element = document.querySelector("#country-id") as FillableFormFieldElement; + const elementLabel = document.querySelector("label[for='country-id']"); + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(document.querySelectorAll).toHaveBeenCalledWith(`label[for="${element.id}"]`); + expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( + new Set([elementLabel]) + ); + expect(labelTag).toEqual("Country"); + }); + + it("queries all labels associated with the element's name", () => { + document.body.innerHTML = ` + + + `; + const element = document.querySelector("select") as FillableFormFieldElement; + const elementLabel = document.querySelector("label[for='country-name']"); + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(document.querySelectorAll).not.toHaveBeenCalledWith(`label[for="${element.id}"]`); + expect(document.querySelectorAll).toHaveBeenCalledWith(`label[for="${element.name}"]`); + expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( + new Set([elementLabel]) + ); + expect(labelTag).toEqual("Country"); + }); + + it("will not add duplicate labels that are found to the label tag", () => { + document.body.innerHTML = ` + +
    + `; + const element = document.querySelector("#country-name") as FillableFormFieldElement; + element.name = "country-name"; + const elementLabel = document.querySelector("label[for='country-name']"); + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(document.querySelectorAll).toHaveBeenCalledWith( + `label[for="${element.id}"], label[for="${element.name}"]` + ); + expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( + new Set([elementLabel]) + ); + expect(labelTag).toEqual("Country"); + }); + + it("will attempt to identify the label of an element from its parent element", () => { + document.body.innerHTML = ``; + const element = document.querySelector("#username-id") as FillableFormFieldElement; + const elementLabel = element.parentElement; + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( + new Set([elementLabel]) + ); + expect(labelTag).toEqual("Username"); + }); + + it("will attempt to identify the label of an element from a `dt` element associated with the element's parent", () => { + document.body.innerHTML = ` +
    +
    Username
    +
    + +
    +
    + `; + const element = document.querySelector("#username-id") as FillableFormFieldElement; + const elementLabel = document.querySelector("#label-element"); + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( + new Set([elementLabel]) + ); + expect(labelTag).toEqual("Username"); + }); + + it("will return an empty string value if no labels can be found for an element", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("#username-id") as FillableFormFieldElement; + + const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); + + expect(labelTag).toEqual(""); + }); + }); + + describe("queryElementLabels", () => { + it("returns null if the passed element has no id or name", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labels = collectAutofillContentService["queryElementLabels"](element); + + expect(labels).toBeNull(); + }); + + it("returns an empty NodeList if the passed element has no label", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labels = collectAutofillContentService["queryElementLabels"](element); + + expect(labels).toEqual(document.querySelectorAll("label")); + }); + + it("returns the label of an element associated with its ID value", () => { + document.body.innerHTML = ` + + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labels = collectAutofillContentService["queryElementLabels"](element); + + expect(labels).toEqual(document.querySelectorAll("label[for='username-id']")); + }); + + it("returns the label of an element associated with its name value", () => { + document.body.innerHTML = ` + + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labels = collectAutofillContentService["queryElementLabels"](element); + + expect(labels).toEqual(document.querySelectorAll("label[for='username']")); + }); + }); + + describe("createLabelElementsTag", () => { + it("returns a string containing all the labels associated with a given input element", () => { + const firstLabelText = "Username by name"; + const secondLabelText = "Username by ID"; + document.body.innerHTML = ` + + + + `; + const labels = document.querySelectorAll("label"); + jest.spyOn(collectAutofillContentService as any, "trimAndRemoveNonPrintableText"); + + const labelTag = collectAutofillContentService["createLabelElementsTag"](new Set(labels)); + + expect( + collectAutofillContentService["trimAndRemoveNonPrintableText"] + ).toHaveBeenNthCalledWith(1, firstLabelText); + expect( + collectAutofillContentService["trimAndRemoveNonPrintableText"] + ).toHaveBeenNthCalledWith(2, secondLabelText); + expect(labelTag).toEqual(`${firstLabelText}${secondLabelText}`); + }); + }); + + describe("getAutofillFieldMaxLength", () => { + it("returns null if the passed FormFieldElement is not an element type that has a max length property", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("select") as FillableFormFieldElement; + + const maxLength = collectAutofillContentService["getAutofillFieldMaxLength"](element); + + expect(maxLength).toBeNull(); + }); + + it("returns a value of 999 if the passed FormFieldElement has no set maxLength value", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const maxLength = collectAutofillContentService["getAutofillFieldMaxLength"](element); + + expect(maxLength).toEqual(999); + }); + + it("returns a value of 999 if the passed FormFieldElement has a maxLength value higher than 999", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const maxLength = collectAutofillContentService["getAutofillFieldMaxLength"](element); + + expect(maxLength).toEqual(999); + }); + + it("returns the maxLength property of a passed FormFieldElement", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const maxLength = collectAutofillContentService["getAutofillFieldMaxLength"](element); + + expect(maxLength).toEqual(10); + }); + }); + + describe("createAutofillFieldRightLabel", () => { + it("returns an empty string if no siblings are found", () => { + document.body.innerHTML = ` + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labelTag = collectAutofillContentService["createAutofillFieldRightLabel"](element); + + expect(labelTag).toEqual(""); + }); + + it("returns the text content of the element's next sibling element", () => { + document.body.innerHTML = ` + + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labelTag = collectAutofillContentService["createAutofillFieldRightLabel"](element); + + expect(labelTag).toEqual("Username"); + }); + + it("returns the text content of the element's next sibling textNode", () => { + document.body.innerHTML = ` + + Username + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labelTag = collectAutofillContentService["createAutofillFieldRightLabel"](element); + + expect(labelTag).toEqual("Username"); + }); + }); + + describe("createAutofillFieldLeftLabel", () => { + it("returns a string value of the text content associated with the previous siblings of the passed element", () => { + document.body.innerHTML = ` +
    + Text Content + + +
    + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labelTag = collectAutofillContentService["createAutofillFieldLeftLabel"](element); + + expect(labelTag).toEqual("Text ContentUsername"); + }); + }); + + describe("createAutofillFieldTopLabel", () => { + it("returns the table column header value for the passed table element", () => { + document.body.innerHTML = ` + + + + + + + + + + + + + +
    UsernamePasswordLogin code
    + `; + const targetTableCellInput = document.querySelector( + 'input[name="password"]' + ) as HTMLInputElement; + + const targetTableCellLabel = + collectAutofillContentService["createAutofillFieldTopLabel"](targetTableCellInput); + + expect(targetTableCellLabel).toEqual("Password"); + }); + + it("will attempt to return the value for the previous sibling row as the label if a `th` cell is not found", () => { + document.body.innerHTML = ` + + + + + + + + + + + + + +
    UsernamePasswordLogin code
    + `; + const targetTableCellInput = document.querySelector( + 'input[name="auth-code"]' + ) as HTMLInputElement; + + const targetTableCellLabel = + collectAutofillContentService["createAutofillFieldTopLabel"](targetTableCellInput); + + expect(targetTableCellLabel).toEqual("Login code"); + }); + + it("returns null for the passed table element it's parent row has no previous sibling row", () => { + document.body.innerHTML = ` + + + + + + + + +
    + `; + const targetTableCellInput = document.querySelector( + 'input[name="password"]' + ) as HTMLInputElement; + + const targetTableCellLabel = + collectAutofillContentService["createAutofillFieldTopLabel"](targetTableCellInput); + + expect(targetTableCellLabel).toEqual(null); + }); + + it("returns null if the input element is not structured within a `td` element", () => { + document.body.innerHTML = ` + + + + + + + + + +
    + + + +
    UsernamePasswordLogin code
    + `; + const targetTableCellInput = document.querySelector( + 'input[name="password"]' + ) as HTMLInputElement; + + const targetTableCellLabel = + collectAutofillContentService["createAutofillFieldTopLabel"](targetTableCellInput); + + expect(targetTableCellLabel).toEqual(null); + }); + + it("returns null if the index of the `td` element is larger than the length of cells in the sibling row", () => { + document.body.innerHTML = ` + + + + + + + + + + + + +
    UsernamePassword
    + `; + const targetTableCellInput = document.querySelector( + 'input[name="auth-code"]' + ) as HTMLInputElement; + + const targetTableCellLabel = + collectAutofillContentService["createAutofillFieldTopLabel"](targetTableCellInput); + + expect(targetTableCellLabel).toEqual(null); + }); + }); + + describe("isNewSectionElement", () => { + const validElementTags = [ + "html", + "body", + "button", + "form", + "head", + "iframe", + "input", + "option", + "script", + "select", + "table", + "textarea", + ]; + const invalidElementTags = ["div", "span"]; + + describe("given a transitional element", () => { + validElementTags.forEach((tag) => { + const element = document.createElement(tag); + + it(`returns true if the element tag is a ${tag}`, () => { + expect(collectAutofillContentService["isNewSectionElement"](element)).toEqual(true); + }); + }); + }); + + describe("given an non-transitional element", () => { + invalidElementTags.forEach((tag) => { + const element = document.createElement(tag); + + it(`returns false if the element tag is a ${tag}`, () => { + expect(collectAutofillContentService["isNewSectionElement"](element)).toEqual(false); + }); + }); + }); + + it(`returns true if the provided element is falsy`, () => { + expect(collectAutofillContentService["isNewSectionElement"](undefined)).toEqual(true); + }); + }); + + describe("getTextContentFromElement", () => { + it("returns the node value for a text node", () => { + document.body.innerHTML = ` +
    + +
    + `; + const element = document.querySelector("#username-id"); + const textNode = element.previousSibling; + const parsedTextContent = collectAutofillContentService["trimAndRemoveNonPrintableText"]( + textNode.nodeValue + ); + jest.spyOn(collectAutofillContentService as any, "trimAndRemoveNonPrintableText"); + + const textContent = collectAutofillContentService["getTextContentFromElement"](textNode); + + expect(textNode.nodeType).toEqual(Node.TEXT_NODE); + expect(collectAutofillContentService["trimAndRemoveNonPrintableText"]).toHaveBeenCalledWith( + textNode.nodeValue + ); + expect(textContent).toEqual(parsedTextContent); + }); + + it("returns the text content for an element node", () => { + document.body.innerHTML = ` +
    + + +
    + `; + const element = document.querySelector('label[for="username-id"]'); + jest.spyOn(collectAutofillContentService as any, "trimAndRemoveNonPrintableText"); + + const textContent = collectAutofillContentService["getTextContentFromElement"](element); + + expect(element.nodeType).toEqual(Node.ELEMENT_NODE); + expect(collectAutofillContentService["trimAndRemoveNonPrintableText"]).toHaveBeenCalledWith( + element.textContent + ); + expect(textContent).toEqual(element.textContent); + }); + }); + + describe("trimAndRemoveNonPrintableText", () => { + it("returns an empty string if no text content is passed", () => { + const textContent = collectAutofillContentService["trimAndRemoveNonPrintableText"](undefined); + + expect(textContent).toEqual(""); + }); + + it("returns a trimmed string with all non-printable text removed", () => { + const nonParsedText = `Hello!\nThis is a \t + test string.\x0B\x08`; + + const parsedText = + collectAutofillContentService["trimAndRemoveNonPrintableText"](nonParsedText); + + expect(parsedText).toEqual("Hello! This is a test string."); + }); + }); + + describe("recursivelyGetTextFromPreviousSiblings", () => { + it("should find text adjacent to the target element likely to be a label", () => { + document.body.innerHTML = ` +
    + Text about things +
    some things
    +
    +

    Stuff Section Header

    + Other things which are also stuff +
    Not visible text
    + + +
    +
    + `; + const textInput = document.querySelector("#input-tag") as FormElementWithAttribute; + + const elementList: string[] = + collectAutofillContentService["recursivelyGetTextFromPreviousSiblings"](textInput); + + expect(elementList).toEqual([ + "something else", + "Not visible text", + "Other things which are also stuff", + "Stuff Section Header", + ]); + }); + + it("should stop looking at siblings for label values when a 'new section' element is seen", () => { + document.body.innerHTML = ` +
    + Text about things +
    some things
    +
    +

    Stuff Section Header

    + Other things which are also stuff +
    Not a label
    + + + +
    +
    + `; + + const textInput = document.querySelector("#input-tag") as FormElementWithAttribute; + const elementList: string[] = + collectAutofillContentService["recursivelyGetTextFromPreviousSiblings"](textInput); + + expect(elementList).toEqual(["something else"]); + }); + + it("should keep looking for labels in parents when there are no siblings of the target element", () => { + document.body.innerHTML = ` +
    + Text about things + +
    some things
    +
    + +
    +
    + `; + + const textInput = document.querySelector("#input-tag") as FormElementWithAttribute; + const elementList: string[] = + collectAutofillContentService["recursivelyGetTextFromPreviousSiblings"](textInput); + + expect(elementList).toEqual(["some things"]); + }); + + it("should find label in parent sibling last child if no other label candidates have been encountered and there are no text nodes along the way", () => { + document.body.innerHTML = ` +
    +
    +
    not the most relevant things
    +
    some nested things
    +
    + +
    +
    +
    + `; + + const textInput = document.querySelector("#input-tag") as FormElementWithAttribute; + const elementList: string[] = + collectAutofillContentService["recursivelyGetTextFromPreviousSiblings"](textInput); + + expect(elementList).toEqual(["some nested things"]); + }); + + it("should exit early if the target element has no parent element/node", () => { + const textInput = document.querySelector("html") as HTMLHtmlElement; + + const elementList: string[] = + collectAutofillContentService["recursivelyGetTextFromPreviousSiblings"](textInput); + + expect(elementList).toEqual([]); + }); + }); + + describe("getPropertyOrAttribute", () => { + it("returns the value of the named property of the target element if the property exists within the element", () => { + document.body.innerHTML += ''; + const textInput = document.querySelector("#username") as HTMLInputElement; + textInput.setAttribute("value", "jsmith"); + const checkboxInput = document.querySelector('input[type="checkbox"]') as HTMLInputElement; + jest.spyOn(textInput, "getAttribute"); + jest.spyOn(checkboxInput, "getAttribute"); + + const textInputValue = collectAutofillContentService["getPropertyOrAttribute"]( + textInput, + "value" + ); + const textInputId = collectAutofillContentService["getPropertyOrAttribute"](textInput, "id"); + const textInputBaseURI = collectAutofillContentService["getPropertyOrAttribute"]( + textInput, + "baseURI" + ); + const textInputAutofocus = collectAutofillContentService["getPropertyOrAttribute"]( + textInput, + "autofocus" + ); + const checkboxInputChecked = collectAutofillContentService["getPropertyOrAttribute"]( + checkboxInput, + "checked" + ); + + expect(textInput.getAttribute).not.toHaveBeenCalled(); + expect(checkboxInput.getAttribute).not.toHaveBeenCalled(); + expect(textInputValue).toEqual("jsmith"); + expect(textInputId).toEqual("username"); + expect(textInputBaseURI).toEqual("http://localhost/"); + expect(textInputAutofocus).toEqual(false); + expect(checkboxInputChecked).toEqual(true); + }); + + it("returns the value of the named attribute of the element if it does not exist as a property within the element", () => { + const textInput = document.querySelector("#username") as HTMLInputElement; + textInput.setAttribute("data-unique-attribute", "unique-value"); + jest.spyOn(textInput, "getAttribute"); + + const textInputUniqueAttribute = collectAutofillContentService["getPropertyOrAttribute"]( + textInput, + "data-unique-attribute" + ); + + expect(textInputUniqueAttribute).toEqual("unique-value"); + expect(textInput.getAttribute).toHaveBeenCalledWith("data-unique-attribute"); + }); + + it("returns a null value if the element does not contain the passed attribute name as either a property or attribute value", () => { + const textInput = document.querySelector("#username") as HTMLInputElement; + jest.spyOn(textInput, "getAttribute"); + + const textInputNonExistentAttribute = collectAutofillContentService["getPropertyOrAttribute"]( + textInput, + "non-existent-attribute" + ); + + expect(textInputNonExistentAttribute).toEqual(null); + expect(textInput.getAttribute).toHaveBeenCalledWith("non-existent-attribute"); + }); + }); + + describe("getElementValue", () => { + it("returns an empty string of passed input elements whose value is not set", () => { + document.body.innerHTML += ` + + + + `; + const textInput = document.querySelector("#username") as HTMLInputElement; + const checkboxInput = document.querySelector('input[type="checkbox"]') as HTMLInputElement; + const hiddenInput = document.querySelector("#hidden-input") as HTMLInputElement; + const spanInput = document.querySelector("#span-input") as HTMLInputElement; + + const textInputValue = collectAutofillContentService["getElementValue"](textInput); + const checkboxInputValue = collectAutofillContentService["getElementValue"](checkboxInput); + const hiddenInputValue = collectAutofillContentService["getElementValue"](hiddenInput); + const spanInputValue = collectAutofillContentService["getElementValue"](spanInput); + + expect(textInputValue).toEqual(""); + expect(checkboxInputValue).toEqual(""); + expect(hiddenInputValue).toEqual(""); + expect(spanInputValue).toEqual(""); + }); + + it("returns the value of the passed input element", () => { + document.body.innerHTML += ` + + + A span input value + `; + const textInput = document.querySelector("#username") as HTMLInputElement; + textInput.value = "jsmith"; + const checkboxInput = document.querySelector('input[type="checkbox"]') as HTMLInputElement; + checkboxInput.checked = true; + const hiddenInput = document.querySelector("#hidden-input") as HTMLInputElement; + hiddenInput.value = "aHiddenInputValue"; + const spanInput = document.querySelector("#span-input") as HTMLInputElement; + + const textInputValue = collectAutofillContentService["getElementValue"](textInput); + const checkboxInputValue = collectAutofillContentService["getElementValue"](checkboxInput); + const hiddenInputValue = collectAutofillContentService["getElementValue"](hiddenInput); + const spanInputValue = collectAutofillContentService["getElementValue"](spanInput); + + expect(textInputValue).toEqual("jsmith"); + expect(checkboxInputValue).toEqual("✓"); + expect(hiddenInputValue).toEqual("aHiddenInputValue"); + expect(spanInputValue).toEqual("A span input value"); + }); + + it("return the truncated value of the passed hidden input type if the value length exceeds 256 characters", () => { + document.body.innerHTML += ` + + `; + const longValueHiddenInput = document.querySelector( + "#long-value-hidden-input" + ) as HTMLInputElement; + + const longHiddenValue = + collectAutofillContentService["getElementValue"](longValueHiddenInput); + + expect(longHiddenValue).toEqual( + "’Twas brillig, and the slithy toves | Did gyre and gimble in the wabe: | All mimsy were the borogoves, | And the mome raths outgrabe. | “Beware the Jabberwock, my son! | The jaws that bite, the claws that catch! | Beware the Jubjub bird, and shun | The f...SNIPPED" + ); + }); + }); + + describe("getSelectElementOptions", () => { + it("returns the inner text and values of each `option` within the passed `select`", () => { + document.body.innerHTML = ` + + + `; + const selectWithOptions = document.querySelector("#select-with-options") as HTMLSelectElement; + const selectWithoutOptions = document.querySelector( + "#select-without-options" + ) as HTMLSelectElement; + + const selectWithOptionsOptions = + collectAutofillContentService["getSelectElementOptions"](selectWithOptions); + const selectWithoutOptionsOptions = + collectAutofillContentService["getSelectElementOptions"](selectWithoutOptions); + + expect(selectWithOptionsOptions).toEqual({ + options: [ + ["option1", "1"], + ["optionb", "b"], + ["optioniii", "iii"], + [null, "four"], + ], + }); + expect(selectWithoutOptionsOptions).toEqual({ options: [] }); + }); + }); +}); diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts new file mode 100644 index 00000000000..ec7658c9863 --- /dev/null +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -0,0 +1,578 @@ +import AutofillField from "../models/autofill-field"; +import AutofillForm from "../models/autofill-form"; +import AutofillPageDetails from "../models/autofill-page-details"; +import { + ElementWithOpId, + FillableFormFieldElement, + FormFieldElement, + FormElementWithAttribute, +} from "../types"; + +import { CollectAutofillContentService as CollectAutofillContentServiceInterface } from "./abstractions/collect-autofill-content.service"; +import DomElementVisibilityService from "./dom-element-visibility.service"; + +class CollectAutofillContentService implements CollectAutofillContentServiceInterface { + private readonly domElementVisibilityService: DomElementVisibilityService; + + constructor(domElementVisibilityService: DomElementVisibilityService) { + this.domElementVisibilityService = domElementVisibilityService; + } + + /** + * Builds the data for all the forms and fields + * that are found within the page DOM. + * @returns {Promise} + * @public + */ + async getPageDetails(): Promise { + const autofillFormsData: Record = this.buildAutofillFormsData(); + const autofillFieldsData: AutofillField[] = await this.buildAutofillFieldsData(); + + return { + title: document.title, + url: (document.defaultView || window).location.href, + documentUrl: document.location.href, + forms: autofillFormsData, + fields: autofillFieldsData, + collectedTimestamp: Date.now(), + }; + } + + /** + * Find an AutofillField element by its opid, will only return the first + * element if there are multiple elements with the same opid. If no + * element is found, null will be returned. + * @param {string} opid + * @returns {FormFieldElement | null} + */ + getAutofillFieldElementByOpid(opid: string): FormFieldElement | null { + const fieldElements = this.getAutofillFieldElements(); + const fieldElementsWithOpid = fieldElements.filter( + (fieldElement) => (fieldElement as ElementWithOpId).opid === opid + ) as ElementWithOpId[]; + + if (!fieldElementsWithOpid.length) { + const elementIndex = parseInt(opid.split("__")[1], 10); + + return fieldElements[elementIndex] || null; + } + + if (fieldElementsWithOpid.length > 1) { + // eslint-disable-next-line no-console + console.warn(`More than one element found with opid ${opid}`); + } + + return fieldElementsWithOpid[0]; + } + + /** + * Queries the DOM for all the forms elements and + * returns a collection of AutofillForm objects. + * @returns {Record} + * @private + */ + private buildAutofillFormsData(): Record { + const autofillForms: Record = {}; + const documentFormElements = document.querySelectorAll("form"); + + documentFormElements.forEach((formElement: HTMLFormElement, index: number) => { + formElement.opid = `__form__${index}`; + + autofillForms[formElement.opid] = { + opid: formElement.opid, + htmlAction: new URL( + this.getPropertyOrAttribute(formElement, "action"), + window.location.href + ).href, + htmlName: this.getPropertyOrAttribute(formElement, "name"), + htmlID: this.getPropertyOrAttribute(formElement, "id"), + htmlMethod: this.getPropertyOrAttribute(formElement, "method"), + }; + }); + + return autofillForms; + } + + /** + * Queries the DOM for all the field elements and + * returns a list of AutofillField objects. + * @returns {Promise} + * @private + */ + private async buildAutofillFieldsData(): Promise { + const autofillFieldElements = this.getAutofillFieldElements(50); + const autofillFieldDataPromises = autofillFieldElements.map(this.buildAutofillFieldItem); + + return Promise.all(autofillFieldDataPromises); + } + + /** + * Queries the DOM for all the field elements that can be autofilled, + * and returns a list limited to the given `fieldsLimit` number that + * is ordered by priority. + * @param {number} fieldsLimit - The maximum number of fields to return + * @returns {FormFieldElement[]} + * @private + */ + private getAutofillFieldElements(fieldsLimit?: number): FormFieldElement[] { + const formFieldElements: FormFieldElement[] = [ + ...(document.querySelectorAll( + 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]):not([type="file"]):not([data-bwignore]), ' + + "textarea:not([data-bwignore]), " + + "select:not([data-bwignore]), " + + "span[data-bwautofill]" + ) as NodeListOf), + ]; + + if (!fieldsLimit || formFieldElements.length <= fieldsLimit) { + return formFieldElements; + } + + const priorityFormFields: FormFieldElement[] = []; + const unimportantFormFields: FormFieldElement[] = []; + const unimportantFieldTypesSet = new Set(["checkbox", "radio"]); + for (const element of formFieldElements) { + if (priorityFormFields.length >= fieldsLimit) { + return priorityFormFields; + } + + const fieldType = this.getPropertyOrAttribute(element, "type")?.toLowerCase(); + if (unimportantFieldTypesSet.has(fieldType)) { + unimportantFormFields.push(element); + continue; + } + + priorityFormFields.push(element); + } + + const numberUnimportantFieldsToInclude = fieldsLimit - priorityFormFields.length; + for (let index = 0; index < numberUnimportantFieldsToInclude; index++) { + priorityFormFields.push(unimportantFormFields[index]); + } + + return priorityFormFields; + } + + /** + * Builds an AutofillField object from the given form element. Will only return + * shared field values if the element is a span element. Will not return any label + * values if the element is a hidden input element. + * @param {ElementWithOpId} element + * @param {number} index + * @returns {Promise} + * @private + */ + private buildAutofillFieldItem = async ( + element: ElementWithOpId, + index: number + ): Promise => { + element.opid = `__${index}`; + + const autofillFieldBase = { + opid: element.opid, + elementNumber: index, + maxLength: this.getAutofillFieldMaxLength(element), + viewable: await this.domElementVisibilityService.isFormFieldViewable(element), + htmlID: this.getPropertyOrAttribute(element, "id"), + htmlName: this.getPropertyOrAttribute(element, "name"), + htmlClass: this.getPropertyOrAttribute(element, "class"), + tabindex: this.getPropertyOrAttribute(element, "tabindex"), + title: this.getPropertyOrAttribute(element, "title"), + tagName: this.getPropertyOrAttribute(element, "tagName")?.toLowerCase(), + }; + + if (element instanceof HTMLSpanElement) { + return autofillFieldBase; + } + + let autofillFieldLabels = {}; + const autoCompleteType = + this.getPropertyOrAttribute(element, "x-autocompletetype") || + this.getPropertyOrAttribute(element, "autocompletetype") || + this.getPropertyOrAttribute(element, "autocomplete"); + const elementType = this.getPropertyOrAttribute(element, "type")?.toLowerCase(); + if (elementType !== "hidden") { + autofillFieldLabels = { + "label-tag": this.createAutofillFieldLabelTag(element), + "label-data": this.getPropertyOrAttribute(element, "data-label"), + "label-aria": this.getPropertyOrAttribute(element, "aria-label"), + "label-top": this.createAutofillFieldTopLabel(element), + "label-right": this.createAutofillFieldRightLabel(element), + "label-left": this.createAutofillFieldLeftLabel(element), + placeholder: this.getPropertyOrAttribute(element, "placeholder"), + }; + } + + return { + ...autofillFieldBase, + ...autofillFieldLabels, + rel: this.getPropertyOrAttribute(element, "rel"), + type: elementType, + value: this.getElementValue(element), + checked: Boolean(this.getPropertyOrAttribute(element, "checked")), + autoCompleteType: autoCompleteType !== "off" ? autoCompleteType : null, + disabled: Boolean(this.getPropertyOrAttribute(element, "disabled")), + readonly: Boolean(this.getPropertyOrAttribute(element, "readOnly")), + selectInfo: + element instanceof HTMLSelectElement ? this.getSelectElementOptions(element) : null, + form: element.form ? this.getPropertyOrAttribute(element.form, "opid") : null, + "aria-hidden": this.getPropertyOrAttribute(element, "aria-hidden") === "true", + "aria-disabled": this.getPropertyOrAttribute(element, "aria-disabled") === "true", + "aria-haspopup": this.getPropertyOrAttribute(element, "aria-haspopup") === "true", + "data-stripe": this.getPropertyOrAttribute(element, "data-stripe"), + }; + }; + + /** + * Creates a label tag used to autofill the element pulled from a label + * associated with the element's id, name, parent element or from an + * associated description term element if no other labels can be found. + * Returns a string containing all the `textContent` or `innerText` + * values of the label elements. + * @param {FillableFormFieldElement} element + * @returns {string} + * @private + */ + private createAutofillFieldLabelTag(element: FillableFormFieldElement): string { + const labelElementsSet: Set = new Set(element.labels); + + if (labelElementsSet.size) { + return this.createLabelElementsTag(labelElementsSet); + } + + const labelElements: NodeListOf | null = this.queryElementLabels(element); + labelElements?.forEach((labelElement) => labelElementsSet.add(labelElement)); + + let currentElement: HTMLElement | null = element; + while (currentElement && currentElement !== document.documentElement) { + if (currentElement instanceof HTMLLabelElement) { + labelElementsSet.add(currentElement); + } + + currentElement = currentElement.parentElement.closest("label"); + } + + if ( + !labelElementsSet.size && + element.parentElement?.tagName.toLowerCase() === "dd" && + element.parentElement.previousElementSibling?.tagName.toLowerCase() === "dt" + ) { + labelElementsSet.add(element.parentElement.previousElementSibling as HTMLElement); + } + + return this.createLabelElementsTag(labelElementsSet); + } + + /** + * Queries the DOM for label elements associated with the given element + * by id or name. Returns a NodeList of label elements or null if none + * are found. + * @param {FillableFormFieldElement} element + * @returns {NodeListOf | null} + * @private + */ + private queryElementLabels( + element: FillableFormFieldElement + ): NodeListOf | null { + let labelQuerySelectors = element.id ? `label[for="${element.id}"]` : ""; + if (element.name) { + const forElementNameSelector = `label[for="${element.name}"]`; + labelQuerySelectors = labelQuerySelectors + ? `${labelQuerySelectors}, ${forElementNameSelector}` + : forElementNameSelector; + } + + if (!labelQuerySelectors) { + return null; + } + + return document.querySelectorAll(labelQuerySelectors); + } + + /** + * Map over all the label elements and creates a + * string of the text content of each label element. + * @param {Set} labelElementsSet + * @returns {string} + * @private + */ + private createLabelElementsTag = (labelElementsSet: Set): string => { + return [...labelElementsSet] + .map((labelElement) => { + const textContent: string | null = labelElement + ? labelElement.textContent || labelElement.innerText + : null; + + return this.trimAndRemoveNonPrintableText(textContent || ""); + }) + .join(""); + }; + + /** + * Gets the maxLength property of the passed FormFieldElement and + * returns the value or null if the element does not have a + * maxLength property. If the element has a maxLength property + * greater than 999, it will return 999. + * @param {FormFieldElement} element + * @returns {number | null} + * @private + */ + private getAutofillFieldMaxLength(element: FormFieldElement): number | null { + const elementHasMaxLengthProperty = + element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement; + const elementMaxLength = + elementHasMaxLengthProperty && element.maxLength > -1 ? element.maxLength : 999; + + return elementHasMaxLengthProperty ? Math.min(elementMaxLength, 999) : null; + } + + /** + * Iterates over the next siblings of the passed element and + * returns a string of the text content of each element. Will + * stop iterating if it encounters a new section element. + * @param {FormFieldElement} element + * @returns {string} + * @private + */ + private createAutofillFieldRightLabel(element: FormFieldElement): string { + const labelTextContent: string[] = []; + let currentElement: ChildNode = element; + + while (currentElement && currentElement.nextSibling) { + currentElement = currentElement.nextSibling; + if (this.isNewSectionElement(currentElement)) { + break; + } + + const textContent = this.getTextContentFromElement(currentElement); + if (textContent) { + labelTextContent.push(textContent); + } + } + + return labelTextContent.join(""); + } + + /** + * Recursively gets the text content from an element's previous siblings + * and returns a string of the text content of each element. + * @param {FormFieldElement} element + * @returns {string} + * @private + */ + private createAutofillFieldLeftLabel(element: FormFieldElement): string { + const labelTextContent: string[] = this.recursivelyGetTextFromPreviousSiblings(element); + + return labelTextContent.reverse().join(""); + } + + /** + * Assumes that the input elements that are to be autofilled are within a + * table structure. Queries the previous sibling of the parent row that + * the input element is in and returns the text content of the cell that + * is in the same column as the input element. + * @param {FormFieldElement} element + * @returns {string | null} + * @private + */ + private createAutofillFieldTopLabel(element: FormFieldElement): string | null { + const tableDataElement = element.closest("td"); + if (!tableDataElement) { + return null; + } + + const tableDataElementIndex = tableDataElement.cellIndex; + const parentSiblingTableRowElement = tableDataElement.closest("tr") + ?.previousElementSibling as HTMLTableRowElement; + + return parentSiblingTableRowElement?.cells?.length > tableDataElementIndex + ? this.getTextContentFromElement(parentSiblingTableRowElement.cells[tableDataElementIndex]) + : null; + } + + /** + * Check if the element's tag indicates that a transition to a new section of the + * page is occurring. If so, we should not use the element or its children in order + * to get autofill context for the previous element. + * @param {HTMLElement} currentElement + * @returns {boolean} + * @private + */ + private isNewSectionElement(currentElement: HTMLElement | Node): boolean { + if (!currentElement) { + return true; + } + + const transitionalElementTagsSet = new Set([ + "html", + "body", + "button", + "form", + "head", + "iframe", + "input", + "option", + "script", + "select", + "table", + "textarea", + ]); + return ( + "tagName" in currentElement && + transitionalElementTagsSet.has(currentElement.tagName.toLowerCase()) + ); + } + + /** + * Gets the text content from a passed element, regardless of whether it is a + * text node, an element node or an HTMLElement. + * @param {Node | HTMLElement} element + * @returns {string} + * @private + */ + private getTextContentFromElement(element: Node | HTMLElement): string { + if (element.nodeType === Node.TEXT_NODE) { + return this.trimAndRemoveNonPrintableText(element.nodeValue); + } + + return this.trimAndRemoveNonPrintableText( + element.textContent || (element as HTMLElement).innerText + ); + } + + /** + * Removes non-printable characters from the passed text + * content and trims leading and trailing whitespace. + * @param {string} textContent + * @returns {string} + * @private + */ + private trimAndRemoveNonPrintableText(textContent: string): string { + return (textContent || "") + .replace(/[^\x20-\x7E]+|\s+/g, " ") // Strip out non-primitive characters and replace multiple spaces with a single space + .trim(); // Trim leading and trailing whitespace + } + + /** + * Get the text content from the previous siblings of the element. If + * no text content is found, recursively get the text content from the + * previous siblings of the parent element. + * @param {FormFieldElement} element + * @returns {string[]} + * @private + */ + private recursivelyGetTextFromPreviousSiblings(element: Node | HTMLElement): string[] { + const textContentItems: string[] = []; + let currentElement = element; + while (currentElement && currentElement.previousSibling) { + // Ensure we are capturing text content from nodes and elements. + currentElement = currentElement.previousSibling; + + if (this.isNewSectionElement(currentElement)) { + return textContentItems; + } + + const textContent = this.getTextContentFromElement(currentElement); + if (textContent) { + textContentItems.push(textContent); + } + } + + if (!currentElement || textContentItems.length) { + return textContentItems; + } + + // Prioritize capturing text content from elements rather than nodes. + currentElement = currentElement.parentElement || currentElement.parentNode; + + let siblingElement = + currentElement instanceof HTMLElement + ? currentElement.previousElementSibling + : currentElement.previousSibling; + while (siblingElement?.lastChild && !this.isNewSectionElement(siblingElement)) { + siblingElement = siblingElement.lastChild; + } + + if (this.isNewSectionElement(siblingElement)) { + return textContentItems; + } + + const textContent = this.getTextContentFromElement(siblingElement); + if (textContent) { + textContentItems.push(textContent); + return textContentItems; + } + + return this.recursivelyGetTextFromPreviousSiblings(siblingElement); + } + + /** + * Get the value of a property or attribute from a FormFieldElement. + * @param {HTMLElement} element + * @param {string} attributeName + * @returns {string | null} + * @private + */ + private getPropertyOrAttribute(element: HTMLElement, attributeName: string): string | null { + if (attributeName in element) { + return (element as FormElementWithAttribute)[attributeName]; + } + + return element.getAttribute(attributeName); + } + + /** + * Gets the value of the element. If the element is a checkbox, returns a checkmark if the + * checkbox is checked, or an empty string if it is not checked. If the element is a hidden + * input, returns the value of the input if it is less than 254 characters, or a truncated + * value if it is longer than 254 characters. + * @param {FormFieldElement} element + * @returns {string} + * @private + */ + private getElementValue(element: FormFieldElement): string { + if (element instanceof HTMLSpanElement) { + const spanTextContent = element.textContent || element.innerText; + return spanTextContent || ""; + } + + const elementValue = element.value || ""; + const elementType = String(element.type).toLowerCase(); + if ("checked" in element && elementType === "checkbox") { + return element.checked ? "✓" : ""; + } + + if (elementType === "hidden") { + const inputValueMaxLength = 254; + + return elementValue.length > inputValueMaxLength + ? `${elementValue.substring(0, inputValueMaxLength)}...SNIPPED` + : elementValue; + } + + return elementValue; + } + + /** + * Get the options from a select element and return them as an array + * of arrays indicating the select element option text and value. + * @param {HTMLSelectElement} element + * @returns {{options: (string | null)[][]}} + * @private + */ + private getSelectElementOptions(element: HTMLSelectElement): { options: (string | null)[][] } { + const options = [...element.options].map((option) => { + const optionText = option.text + ? String(option.text) + .toLowerCase() + .replace(/[\s~`!@$%^&#*()\-_+=:;'"[\]|\\,<.>?]/gm, "") // Remove whitespace and punctuation + : null; + + return [optionText, option.value]; + }); + + return { options }; + } +} + +export default CollectAutofillContentService; diff --git a/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts b/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts new file mode 100644 index 00000000000..e17783b7a65 --- /dev/null +++ b/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts @@ -0,0 +1,409 @@ +import { FormFieldElement } from "../types"; + +import DomElementVisibilityService from "./dom-element-visibility.service"; + +function createBoundingClientRectMock(customProperties: Partial = {}): DOMRectReadOnly { + return { + top: 0, + bottom: 0, + left: 0, + right: 0, + width: 500, + height: 500, + x: 0, + y: 0, + toJSON: jest.fn(), + ...customProperties, + }; +} + +describe("DomElementVisibilityService", () => { + let domElementVisibilityService: DomElementVisibilityService; + + beforeEach(() => { + document.body.innerHTML = ` +
    + + + + +
    + `; + domElementVisibilityService = new DomElementVisibilityService(); + }); + + afterEach(() => { + jest.clearAllMocks(); + document.body.innerHTML = ""; + }); + + describe("isFormFieldViewable", () => { + it("returns false if the element is outside viewport bounds", async () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + jest.spyOn(usernameElement, "getBoundingClientRect"); + jest + .spyOn(domElementVisibilityService as any, "isElementOutsideViewportBounds") + .mockResolvedValueOnce(true); + jest.spyOn(domElementVisibilityService, "isElementHiddenByCss"); + jest.spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement"); + + const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( + usernameElement + ); + + expect(isFormFieldViewable).toEqual(false); + expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); + expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( + usernameElement, + usernameElement.getBoundingClientRect() + ); + expect(domElementVisibilityService["isElementHiddenByCss"]).not.toHaveBeenCalled(); + expect( + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + ).not.toHaveBeenCalled(); + }); + + it("returns false if the element is hidden by CSS", async () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + jest.spyOn(usernameElement, "getBoundingClientRect"); + jest + .spyOn(domElementVisibilityService as any, "isElementOutsideViewportBounds") + .mockReturnValueOnce(false); + jest.spyOn(domElementVisibilityService, "isElementHiddenByCss").mockReturnValueOnce(true); + jest.spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement"); + + const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( + usernameElement + ); + + expect(isFormFieldViewable).toEqual(false); + expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); + expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( + usernameElement, + usernameElement.getBoundingClientRect() + ); + expect(domElementVisibilityService["isElementHiddenByCss"]).toHaveBeenCalledWith( + usernameElement + ); + expect( + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + ).not.toHaveBeenCalled(); + }); + + it("returns false if the element is hidden behind another element", async () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + jest.spyOn(usernameElement, "getBoundingClientRect"); + jest + .spyOn(domElementVisibilityService as any, "isElementOutsideViewportBounds") + .mockReturnValueOnce(false); + jest.spyOn(domElementVisibilityService, "isElementHiddenByCss").mockReturnValueOnce(false); + jest + .spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement") + .mockReturnValueOnce(false); + + const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( + usernameElement + ); + + expect(isFormFieldViewable).toEqual(false); + expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); + expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( + usernameElement, + usernameElement.getBoundingClientRect() + ); + expect(domElementVisibilityService["isElementHiddenByCss"]).toHaveBeenCalledWith( + usernameElement + ); + expect( + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + ).toHaveBeenCalledWith(usernameElement, usernameElement.getBoundingClientRect()); + }); + + it("returns true if the form field is viewable", async () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + jest.spyOn(usernameElement, "getBoundingClientRect"); + jest + .spyOn(domElementVisibilityService as any, "isElementOutsideViewportBounds") + .mockReturnValueOnce(false); + jest.spyOn(domElementVisibilityService, "isElementHiddenByCss").mockReturnValueOnce(false); + jest + .spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement") + .mockReturnValueOnce(true); + + const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( + usernameElement + ); + + expect(isFormFieldViewable).toEqual(true); + expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); + expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( + usernameElement, + usernameElement.getBoundingClientRect() + ); + expect(domElementVisibilityService["isElementHiddenByCss"]).toHaveBeenCalledWith( + usernameElement + ); + expect( + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + ).toHaveBeenCalledWith(usernameElement, usernameElement.getBoundingClientRect()); + }); + }); + + describe("isElementHiddenByCss", () => { + it("returns true when a non-hidden element is passed", () => { + document.body.innerHTML = ` + + `; + const usernameElement = document.getElementById("username"); + + const isElementHidden = domElementVisibilityService["isElementHiddenByCss"](usernameElement); + + expect(isElementHidden).toEqual(false); + }); + + it("returns true when the element has a `visibility: hidden;` CSS rule applied to it either inline or in a computed style", () => { + document.body.innerHTML = ` + + + + `; + const usernameElement = document.getElementById("username"); + const passwordElement = document.getElementById("password"); + jest.spyOn(usernameElement.style, "getPropertyValue"); + jest.spyOn(usernameElement.ownerDocument.defaultView, "getComputedStyle"); + jest.spyOn(passwordElement.style, "getPropertyValue"); + jest.spyOn(passwordElement.ownerDocument.defaultView, "getComputedStyle"); + + const isUsernameElementHidden = + domElementVisibilityService["isElementHiddenByCss"](usernameElement); + const isPasswordElementHidden = + domElementVisibilityService["isElementHiddenByCss"](passwordElement); + + expect(isUsernameElementHidden).toEqual(true); + expect(usernameElement.style.getPropertyValue).toHaveBeenCalled(); + expect(usernameElement.ownerDocument.defaultView.getComputedStyle).toHaveBeenCalledWith( + usernameElement + ); + expect(isPasswordElementHidden).toEqual(true); + expect(passwordElement.style.getPropertyValue).toHaveBeenCalled(); + expect(passwordElement.ownerDocument.defaultView.getComputedStyle).toHaveBeenCalledWith( + passwordElement + ); + }); + + it("returns true when the element has a `display: none;` CSS rule applied to it either inline or in a computed style", () => { + document.body.innerHTML = ` + + + + `; + const usernameElement = document.getElementById("username"); + const passwordElement = document.getElementById("password"); + + const isUsernameElementHidden = + domElementVisibilityService["isElementHiddenByCss"](usernameElement); + const isPasswordElementHidden = + domElementVisibilityService["isElementHiddenByCss"](passwordElement); + + expect(isUsernameElementHidden).toEqual(true); + expect(isPasswordElementHidden).toEqual(true); + }); + + it("returns true when the element has a `opacity: 0;` CSS rule applied to it either inline or in a computed style", () => { + document.body.innerHTML = ` + + + + `; + const usernameElement = document.getElementById("username"); + const passwordElement = document.getElementById("password"); + + const isUsernameElementHidden = + domElementVisibilityService["isElementHiddenByCss"](usernameElement); + const isPasswordElementHidden = + domElementVisibilityService["isElementHiddenByCss"](passwordElement); + + expect(isUsernameElementHidden).toEqual(true); + expect(isPasswordElementHidden).toEqual(true); + }); + + it("returns true when the element has a `clip-path` CSS rule applied to it that hides the element either inline or in a computed style", () => { + document.body.innerHTML = ` + + + + + `; + }); + }); + + describe("isElementOutsideViewportBounds", () => { + const mockViewportWidth = 1920; + const mockViewportHeight = 1080; + + beforeEach(() => { + Object.defineProperty(document.documentElement, "scrollWidth", { + writable: true, + value: mockViewportWidth, + }); + Object.defineProperty(document.documentElement, "scrollHeight", { + writable: true, + value: mockViewportHeight, + }); + }); + + it("returns true if the passed element's size is not sufficient for visibility", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const elementBoundingClientRect = createBoundingClientRectMock({ + width: 9, + height: 9, + }); + + const isElementOutsideViewportBounds = domElementVisibilityService[ + "isElementOutsideViewportBounds" + ](usernameElement, elementBoundingClientRect); + + expect(isElementOutsideViewportBounds).toEqual(true); + }); + + it("returns true if the passed element is overflowing the left viewport", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const elementBoundingClientRect = createBoundingClientRectMock({ + left: -1, + }); + + const isElementOutsideViewportBounds = domElementVisibilityService[ + "isElementOutsideViewportBounds" + ](usernameElement, elementBoundingClientRect); + + expect(isElementOutsideViewportBounds).toEqual(true); + }); + + it("returns true if the passed element is overflowing the right viewport", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const elementBoundingClientRect = createBoundingClientRectMock({ + left: mockViewportWidth + 1, + }); + + const isElementOutsideViewportBounds = domElementVisibilityService[ + "isElementOutsideViewportBounds" + ](usernameElement, elementBoundingClientRect); + + expect(isElementOutsideViewportBounds).toEqual(true); + }); + + it("returns true if the passed element is overflowing the top viewport", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const elementBoundingClientRect = createBoundingClientRectMock({ + top: -1, + }); + + const isElementOutsideViewportBounds = domElementVisibilityService[ + "isElementOutsideViewportBounds" + ](usernameElement, elementBoundingClientRect); + + expect(isElementOutsideViewportBounds).toEqual(true); + }); + + it("returns true if the passed element is overflowing the bottom viewport", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const elementBoundingClientRect = createBoundingClientRectMock({ + top: mockViewportHeight + 1, + }); + + const isElementOutsideViewportBounds = domElementVisibilityService[ + "isElementOutsideViewportBounds" + ](usernameElement, elementBoundingClientRect); + + expect(isElementOutsideViewportBounds).toEqual(true); + }); + + it("returns false if the passed element is not outside of the viewport bounds", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const elementBoundingClientRect = createBoundingClientRectMock({}); + + const isElementOutsideViewportBounds = domElementVisibilityService[ + "isElementOutsideViewportBounds" + ](usernameElement, elementBoundingClientRect); + + expect(isElementOutsideViewportBounds).toEqual(false); + }); + }); + + describe("formFieldIsNotHiddenBehindAnotherElement", () => { + it("returns true if the element found at the center point of the passed targetElement is the targetElement itself", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + jest.spyOn(usernameElement, "getBoundingClientRect"); + document.elementFromPoint = jest.fn(() => usernameElement); + + const formFieldIsNotHiddenBehindAnotherElement = + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"](usernameElement); + + expect(formFieldIsNotHiddenBehindAnotherElement).toEqual(true); + expect(document.elementFromPoint).toHaveBeenCalled(); + expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); + }); + + it("returns true if the element found at the center point of the passed targetElement is an implicit label of the element", () => { + document.body.innerHTML = ` + + `; + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const labelTextElement = document.querySelector("span"); + document.elementFromPoint = jest.fn(() => labelTextElement); + + const formFieldIsNotHiddenBehindAnotherElement = + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"](usernameElement); + + expect(formFieldIsNotHiddenBehindAnotherElement).toEqual(true); + }); + + it("returns true if the element found at the center point of the passed targetElement is a label of the targetElement", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + const labelElement = document.querySelector("label[for='username']") as FormFieldElement; + const mockBoundingRect = createBoundingClientRectMock({}); + jest.spyOn(usernameElement, "getBoundingClientRect"); + document.elementFromPoint = jest.fn(() => labelElement); + + const formFieldIsNotHiddenBehindAnotherElement = domElementVisibilityService[ + "formFieldIsNotHiddenBehindAnotherElement" + ](usernameElement, mockBoundingRect); + + expect(formFieldIsNotHiddenBehindAnotherElement).toEqual(true); + expect(document.elementFromPoint).toHaveBeenCalledWith( + mockBoundingRect.left + mockBoundingRect.width / 2, + mockBoundingRect.top + mockBoundingRect.height / 2 + ); + expect(usernameElement.getBoundingClientRect).not.toHaveBeenCalled(); + }); + + it("returns false if the element found at the center point is not the passed targetElement or a label of that element", () => { + const usernameElement = document.querySelector("input[name='username']") as FormFieldElement; + document.elementFromPoint = jest.fn(() => document.createElement("div")); + + const formFieldIsNotHiddenBehindAnotherElement = + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"](usernameElement); + + expect(formFieldIsNotHiddenBehindAnotherElement).toEqual(false); + }); + }); +}); diff --git a/apps/browser/src/autofill/services/dom-element-visibility.service.ts b/apps/browser/src/autofill/services/dom-element-visibility.service.ts new file mode 100644 index 00000000000..4be59d7f276 --- /dev/null +++ b/apps/browser/src/autofill/services/dom-element-visibility.service.ts @@ -0,0 +1,199 @@ +import { FillableFormFieldElement, FormFieldElement } from "../types"; + +import { DomElementVisibilityService as domElementVisibilityServiceInterface } from "./abstractions/dom-element-visibility.service"; + +class DomElementVisibilityService implements domElementVisibilityServiceInterface { + private cachedComputedStyle: CSSStyleDeclaration | null = null; + + /** + * Checks if a form field is viewable. This is done by checking if the element is within the + * viewport bounds, not hidden by CSS, and not hidden behind another element. + * @param {FormFieldElement} element + * @returns {Promise} + */ + async isFormFieldViewable(element: FormFieldElement): Promise { + const elementBoundingClientRect = element.getBoundingClientRect(); + + if ( + this.isElementOutsideViewportBounds(element, elementBoundingClientRect) || + this.isElementHiddenByCss(element) + ) { + return false; + } + + return this.formFieldIsNotHiddenBehindAnotherElement(element, elementBoundingClientRect); + } + + /** + * Check if the target element is hidden using CSS. This is done by checking the opacity, display, + * visibility, and clip-path CSS properties of the element. We also check the opacity of all + * parent elements to ensure that the target element is not hidden by a parent element. + * @param {HTMLElement} element + * @returns {boolean} + * @public + */ + isElementHiddenByCss(element: HTMLElement): boolean { + this.cachedComputedStyle = null; + + if ( + this.isElementInvisible(element) || + this.isElementNotDisplayed(element) || + this.isElementNotVisible(element) || + this.isElementClipped(element) + ) { + return true; + } + + let parentElement = element.parentElement; + while (parentElement && parentElement !== element.ownerDocument.documentElement) { + this.cachedComputedStyle = null; + if (this.isElementInvisible(parentElement)) { + return true; + } + + parentElement = parentElement.parentElement; + } + + return false; + } + + /** + * Gets the computed style of a given element, will only calculate the computed + * style if the element's style has not been previously cached. + * @param {HTMLElement} element + * @param {string} styleProperty + * @returns {string} + * @private + */ + private getElementStyle(element: HTMLElement, styleProperty: string): string { + if (!this.cachedComputedStyle) { + this.cachedComputedStyle = (element.ownerDocument.defaultView || window).getComputedStyle( + element + ); + } + + return this.cachedComputedStyle.getPropertyValue(styleProperty); + } + + /** + * Checks if the opacity of the target element is less than 0.1. + * @param {HTMLElement} element + * @returns {boolean} + * @private + */ + private isElementInvisible(element: HTMLElement): boolean { + return parseFloat(this.getElementStyle(element, "opacity")) < 0.1; + } + + /** + * Checks if the target element has a display property of none. + * @param {HTMLElement} element + * @returns {boolean} + * @private + */ + private isElementNotDisplayed(element: HTMLElement): boolean { + return this.getElementStyle(element, "display") === "none"; + } + + /** + * Checks if the target element has a visibility property of hidden or collapse. + * @param {HTMLElement} element + * @returns {boolean} + * @private + */ + private isElementNotVisible(element: HTMLElement): boolean { + return new Set(["hidden", "collapse"]).has(this.getElementStyle(element, "visibility")); + } + + /** + * Checks if the target element has a clip-path property that hides the element. + * @param {HTMLElement} element + * @returns {boolean} + * @private + */ + private isElementClipped(element: HTMLElement): boolean { + return new Set([ + "inset(50%)", + "inset(100%)", + "circle(0)", + "circle(0px)", + "circle(0px at 50% 50%)", + "polygon(0 0, 0 0, 0 0, 0 0)", + "polygon(0px 0px, 0px 0px, 0px 0px, 0px 0px)", + ]).has(this.getElementStyle(element, "clipPath")); + } + + /** + * Checks if the target element is outside the viewport bounds. This is done by checking if the + * element is too small or is overflowing the viewport bounds. + * @param {HTMLElement} targetElement + * @param {DOMRectReadOnly | null} targetElementBoundingClientRect + * @returns {boolean} + * @private + */ + private isElementOutsideViewportBounds( + targetElement: HTMLElement, + targetElementBoundingClientRect: DOMRectReadOnly | null = null + ): boolean { + const documentElement = targetElement.ownerDocument.documentElement; + const documentElementWidth = documentElement.scrollWidth; + const documentElementHeight = documentElement.scrollHeight; + const elementBoundingClientRect = + targetElementBoundingClientRect || targetElement.getBoundingClientRect(); + const elementTopOffset = elementBoundingClientRect.top - documentElement.clientTop; + const elementLeftOffset = elementBoundingClientRect.left - documentElement.clientLeft; + + const isElementSizeInsufficient = + elementBoundingClientRect.width < 10 || elementBoundingClientRect.height < 10; + const isElementOverflowingLeftViewport = elementLeftOffset < 0; + const isElementOverflowingRightViewport = + elementLeftOffset + elementBoundingClientRect.width > documentElementWidth; + const isElementOverflowingTopViewport = elementTopOffset < 0; + const isElementOverflowingBottomViewport = + elementTopOffset + elementBoundingClientRect.height > documentElementHeight; + + return ( + isElementSizeInsufficient || + isElementOverflowingLeftViewport || + isElementOverflowingRightViewport || + isElementOverflowingTopViewport || + isElementOverflowingBottomViewport + ); + } + + /** + * Checks if a passed FormField is not hidden behind another element. This is done by + * checking if the element at the center point of the FormField is the FormField itself + * or one of its labels. + * @param {FormFieldElement} targetElement + * @param {DOMRectReadOnly | null} targetElementBoundingClientRect + * @returns {boolean} + * @private + */ + private formFieldIsNotHiddenBehindAnotherElement( + targetElement: FormFieldElement, + targetElementBoundingClientRect: DOMRectReadOnly | null = null + ): boolean { + const elementBoundingClientRect = + targetElementBoundingClientRect || targetElement.getBoundingClientRect(); + const elementAtCenterPoint = targetElement.ownerDocument.elementFromPoint( + elementBoundingClientRect.left + elementBoundingClientRect.width / 2, + elementBoundingClientRect.top + elementBoundingClientRect.height / 2 + ); + + if (elementAtCenterPoint === targetElement) { + return true; + } + + const targetElementLabelsSet = new Set((targetElement as FillableFormFieldElement).labels); + if (targetElementLabelsSet.has(elementAtCenterPoint as HTMLLabelElement)) { + return true; + } + + const closestParentLabel = elementAtCenterPoint?.parentElement?.closest("label"); + + return targetElementLabelsSet.has(closestParentLabel); + } +} + +export default DomElementVisibilityService; diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts new file mode 100644 index 00000000000..828d768ca25 --- /dev/null +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts @@ -0,0 +1,1047 @@ +import { EVENTS } from "../constants"; +import AutofillScript, { FillScript, FillScriptActions } from "../models/autofill-script"; +import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; + +import CollectAutofillContentService from "./collect-autofill-content.service"; +import DomElementVisibilityService from "./dom-element-visibility.service"; +import InsertAutofillContentService from "./insert-autofill-content.service"; + +const mockLoginForm = ` +
    +
    + + +
    +
    +`; + +const eventsToTest = [ + EVENTS.CHANGE, + EVENTS.INPUT, + EVENTS.KEYDOWN, + EVENTS.KEYPRESS, + EVENTS.KEYUP, + "blur", + "click", + "focus", + "focusin", + "focusout", + "mousedown", + "paste", + "select", + "selectionchange", + "touchend", + "touchstart", +]; + +const initEventCount = Object.freeze( + eventsToTest.reduce( + (eventCounts, eventName) => ({ + ...eventCounts, + [eventName]: 0, + }), + {} + ) +); + +let confirmSpy: jest.SpyInstance; +let windowSpy: jest.SpyInstance; +let savedURLs: string[] | null = ["https://bitwarden.com"]; +function setMockWindowLocation({ + protocol, + hostname, +}: { + protocol: "http:" | "https:"; + hostname: string; +}) { + windowSpy.mockImplementation(() => ({ + location: { + protocol, + hostname, + }, + })); +} + +describe("InsertAutofillContentService", () => { + const domElementVisibilityService = new DomElementVisibilityService(); + const collectAutofillContentService = new CollectAutofillContentService( + domElementVisibilityService + ); + let insertAutofillContentService: InsertAutofillContentService; + let fillScript: AutofillScript; + + beforeEach(() => { + document.body.innerHTML = mockLoginForm; + confirmSpy = jest.spyOn(window, "confirm"); + windowSpy = jest.spyOn(window, "window", "get"); + insertAutofillContentService = new InsertAutofillContentService( + domElementVisibilityService, + collectAutofillContentService + ); + fillScript = { + script: [ + ["click_on_opid", "username"], + ["focus_by_opid", "username"], + ["fill_by_opid", "username", "test"], + ], + properties: { + delay_between_operations: 20, + }, + metadata: {}, + autosubmit: null, + savedUrls: ["https://bitwarden.com"], + untrustedIframe: false, + itemType: "login", + }; + }); + + afterEach(() => { + jest.resetAllMocks(); + windowSpy.mockRestore(); + confirmSpy.mockRestore(); + document.body.innerHTML = ""; + }); + + describe("fillForm", () => { + it("returns early if the passed fill script does not have a script property", () => { + fillScript.script = []; + jest.spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe"); + jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill"); + jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill"); + jest.spyOn(insertAutofillContentService as any, "runFillScriptAction"); + + insertAutofillContentService.fillForm(fillScript); + + expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).not.toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledInsecureUrlAutofill"] + ).not.toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + ).not.toHaveBeenCalled(); + expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); + }); + + it("returns early if the script is filling within a sand boxed iframe", () => { + jest + .spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe") + .mockReturnValue(true); + jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill"); + jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill"); + jest.spyOn(insertAutofillContentService as any, "runFillScriptAction"); + + insertAutofillContentService.fillForm(fillScript); + + expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledInsecureUrlAutofill"] + ).not.toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + ).not.toHaveBeenCalled(); + expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); + }); + + it("returns early if the autofill is occurring on an insecure url and the user cancels the autofill", () => { + jest + .spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe") + .mockReturnValue(false); + jest + .spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill") + .mockReturnValue(true); + jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill"); + jest.spyOn(insertAutofillContentService as any, "runFillScriptAction"); + + insertAutofillContentService.fillForm(fillScript); + + expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); + expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + ).not.toHaveBeenCalled(); + expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); + }); + + it("returns early if the iframe is untrusted and the user cancelled the autofill", () => { + jest + .spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe") + .mockReturnValue(false); + jest + .spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill") + .mockReturnValue(false); + jest + .spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill") + .mockReturnValue(true); + jest.spyOn(insertAutofillContentService as any, "runFillScriptAction"); + + insertAutofillContentService.fillForm(fillScript); + + expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); + expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + ).toHaveBeenCalled(); + expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); + }); + + it("runs the fill script action for all scripts found within the fill script", () => { + jest + .spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe") + .mockReturnValue(false); + jest + .spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill") + .mockReturnValue(false); + jest + .spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill") + .mockReturnValue(false); + jest.spyOn(insertAutofillContentService as any, "runFillScriptAction"); + + insertAutofillContentService.fillForm(fillScript); + + expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); + expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); + expect( + insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + ).toHaveBeenCalled(); + expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenCalledTimes(3); + expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( + 1, + fillScript.script[0], + 0, + fillScript.script + ); + expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( + 2, + fillScript.script[1], + 1, + fillScript.script + ); + expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( + 3, + fillScript.script[2], + 2, + fillScript.script + ); + }); + }); + + describe("fillingWithinSandboxedIframe", () => { + afterEach(() => { + Object.defineProperty(globalThis, "window", { + value: { frameElement: null }, + writable: true, + }); + }); + + it("returns false if the `self.origin` value is not null", () => { + const result = insertAutofillContentService["fillingWithinSandboxedIframe"](); + + expect(result).toBe(false); + expect(self.origin).not.toBeNull(); + }); + + it("returns true if the frameElement has a sandbox attribute", () => { + Object.defineProperty(globalThis, "window", { + value: { frameElement: { hasAttribute: jest.fn(() => true) } }, + writable: true, + }); + + const result = insertAutofillContentService["fillingWithinSandboxedIframe"](); + + expect(result).toBe(true); + }); + + it("returns true if the window location hostname is empty", () => { + setMockWindowLocation({ protocol: "http:", hostname: "" }); + + const result = insertAutofillContentService["fillingWithinSandboxedIframe"](); + + expect(result).toBe(true); + }); + }); + + describe("userCancelledInsecureUrlAutofill", () => { + const currentHostname = "bitwarden.com"; + + beforeEach(() => { + savedURLs = [`https://${currentHostname}`]; + }); + + describe("returns false if Autofill occurring...", () => { + it("when there are no saved URLs", () => { + savedURLs = []; + setMockWindowLocation({ protocol: "http:", hostname: currentHostname }); + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(userCancelledInsecureUrlAutofill).toBe(false); + + savedURLs = null; + + const userCancelledInsecureUrlAutofill2 = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).not.toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill2).toBe(false); + }); + + it("on http page and saved URLs contain no https values", () => { + savedURLs = ["http://bitwarden.com"]; + setMockWindowLocation({ protocol: "http:", hostname: currentHostname }); + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).not.toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill).toBe(false); + }); + + it("on https page with saved https URL", () => { + setMockWindowLocation({ protocol: "https:", hostname: currentHostname }); + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).not.toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill).toBe(false); + }); + + it("on page with no password field", () => { + setMockWindowLocation({ protocol: "https:", hostname: currentHostname }); + + document.body.innerHTML = ` +
    +
    + +
    +
    + `; + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).not.toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill).toBe(false); + }); + + it("on http page with saved https URL and user approval", () => { + setMockWindowLocation({ protocol: "http:", hostname: currentHostname }); + confirmSpy.mockImplementation(jest.fn(() => true)); + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill).toBe(false); + }); + }); + + it("returns true if Autofill occurring on http page with saved https URL and user disapproval", () => { + setMockWindowLocation({ protocol: "http:", hostname: currentHostname }); + confirmSpy.mockImplementation(jest.fn(() => false)); + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill).toBe(true); + }); + + it("returns false if the vault item contains uris with both secure and insecure uris, but a insecure uri is being used on a insecure web page", () => { + setMockWindowLocation({ protocol: "http:", hostname: currentHostname }); + savedURLs = ["http://bitwarden.com", "https://some-other-uri.com"]; + + const userCancelledInsecureUrlAutofill = + insertAutofillContentService["userCancelledInsecureUrlAutofill"](savedURLs); + + expect(confirmSpy).not.toHaveBeenCalled(); + expect(userCancelledInsecureUrlAutofill).toBe(false); + }); + }); + + describe("userCancelledUntrustedIframeAutofill", () => { + it("returns false if Autofill occurring within a trusted iframe", () => { + fillScript.untrustedIframe = false; + + const result = + insertAutofillContentService["userCancelledUntrustedIframeAutofill"](fillScript); + + expect(result).toBe(false); + expect(confirmSpy).not.toHaveBeenCalled(); + }); + + it("returns false if Autofill occurring within an untrusted iframe and the user approves", () => { + fillScript.untrustedIframe = true; + confirmSpy.mockImplementation(jest.fn(() => true)); + + const result = + insertAutofillContentService["userCancelledUntrustedIframeAutofill"](fillScript); + + expect(result).toBe(false); + expect(confirmSpy).toHaveBeenCalled(); + }); + + it("returns true if Autofill occurring within an untrusted iframe and the user disapproves", () => { + fillScript.untrustedIframe = true; + confirmSpy.mockImplementation(jest.fn(() => false)); + + const result = + insertAutofillContentService["userCancelledUntrustedIframeAutofill"](fillScript); + + expect(result).toBe(true); + expect(confirmSpy).toHaveBeenCalled(); + }); + }); + + describe("runFillScriptAction", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + it("returns early if no opid is provided", () => { + const action = "fill_by_opid"; + const opid = ""; + const value = "value"; + const scriptAction: FillScript = [action, opid, value]; + jest.spyOn(insertAutofillContentService["autofillInsertActions"], action); + + insertAutofillContentService["runFillScriptAction"](scriptAction, 0); + jest.advanceTimersByTime(20); + + expect(insertAutofillContentService["autofillInsertActions"][action]).not.toHaveBeenCalled(); + }); + + describe("given a valid fill script action and opid", () => { + const fillScriptActions: FillScriptActions[] = [ + "fill_by_opid", + "click_on_opid", + "focus_by_opid", + ]; + fillScriptActions.forEach((action) => { + it(`triggers a ${action} action`, () => { + const opid = "opid"; + const value = "value"; + const scriptAction: FillScript = [action, opid, value]; + jest.spyOn(insertAutofillContentService["autofillInsertActions"], action); + + insertAutofillContentService["runFillScriptAction"](scriptAction, 0); + jest.advanceTimersByTime(20); + + expect( + insertAutofillContentService["autofillInsertActions"][action] + ).toHaveBeenCalledWith({ + opid, + value, + }); + }); + }); + }); + }); + + describe("handleFillFieldByOpidAction", () => { + it("finds the field element by opid and inserts the value into the field", () => { + const opid = "__1"; + const value = "value"; + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + textInput.opid = opid; + textInput.value = value; + jest.spyOn( + insertAutofillContentService["collectAutofillContentService"], + "getAutofillFieldElementByOpid" + ); + jest.spyOn(insertAutofillContentService as any, "insertValueIntoField"); + + insertAutofillContentService["handleFillFieldByOpidAction"](opid, value); + + expect( + insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid + ).toHaveBeenCalledWith(opid); + expect(insertAutofillContentService["insertValueIntoField"]).toHaveBeenCalledWith( + textInput, + value + ); + }); + }); + + describe("handleClickOnFieldByOpidAction", () => { + it("clicks on the elements targeted by the passed opid", () => { + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + textInput.opid = "__1"; + let clickEventCount = 0; + const expectedClickEventCount = 1; + const clickEventHandler: (handledEvent: Event) => void = (handledEvent) => { + const eventTarget = handledEvent.target as HTMLInputElement; + + if (eventTarget.id === "username") { + clickEventCount++; + } + }; + textInput.addEventListener("click", clickEventHandler); + jest.spyOn( + insertAutofillContentService["collectAutofillContentService"], + "getAutofillFieldElementByOpid" + ); + jest.spyOn(insertAutofillContentService as any, "triggerClickOnElement"); + + insertAutofillContentService["handleClickOnFieldByOpidAction"]("__1"); + + expect( + insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid + ).toBeCalledWith("__1"); + expect((insertAutofillContentService as any)["triggerClickOnElement"]).toHaveBeenCalledWith( + textInput + ); + expect(clickEventCount).toBe(expectedClickEventCount); + + textInput.removeEventListener("click", clickEventHandler); + }); + + it("should not trigger click when no suitable elements can be found", () => { + const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + let clickEventCount = 0; + const expectedClickEventCount = 0; + const clickEventHandler: (handledEvent: Event) => void = (handledEvent) => { + const eventTarget = handledEvent.target as HTMLInputElement; + + if (eventTarget.id === "username") { + clickEventCount++; + } + }; + textInput.addEventListener("click", clickEventHandler); + + insertAutofillContentService["handleClickOnFieldByOpidAction"]("__2"); + + expect(clickEventCount).toEqual(expectedClickEventCount); + + textInput.removeEventListener("click", clickEventHandler); + }); + }); + + describe("handleFocusOnFieldByOpidAction", () => { + it("simulates click and focus events on the element targeted by the passed opid", () => { + const targetInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + targetInput.opid = "__0"; + const elementEventCount: { [key: string]: number } = { + ...initEventCount, + }; + // Testing all the relevant events to ensure downstream side-effects are firing correctly + const expectedElementEventCount: { [key: string]: number } = { + ...initEventCount, + click: 1, + focus: 1, + focusin: 1, + }; + const eventHandlers: { [key: string]: EventListener } = {}; + eventsToTest.forEach((eventType) => { + eventHandlers[eventType] = (handledEvent) => { + elementEventCount[handledEvent.type]++; + }; + targetInput.addEventListener(eventType, eventHandlers[eventType]); + }); + jest.spyOn( + insertAutofillContentService["collectAutofillContentService"], + "getAutofillFieldElementByOpid" + ); + jest.spyOn( + insertAutofillContentService as any, + "simulateUserMouseClickAndFocusEventInteractions" + ); + + insertAutofillContentService["handleFocusOnFieldByOpidAction"]("__0"); + + expect( + insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid + ).toBeCalledWith("__0"); + expect( + insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"] + ).toHaveBeenCalledWith(targetInput, true); + expect(elementEventCount).toEqual(expectedElementEventCount); + }); + }); + + describe("insertValueIntoField", () => { + it("returns early if an element is not provided", () => { + const value = "test"; + const element: FormFieldElement | null = null; + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + insertAutofillContentService["insertValueIntoField"](element, value); + + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).not.toHaveBeenCalled(); + }); + + it("returns early if a value is not provided", () => { + const value = ""; + const element: FormFieldElement | null = document.querySelector('input[type="text"]'); + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + insertAutofillContentService["insertValueIntoField"](element, value); + + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).not.toHaveBeenCalled(); + }); + + it("will set the inner text of the element if a span element is passed", () => { + document.body.innerHTML = ``; + const value = "test"; + const element = document.getElementById("username") as FormFieldElement; + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + insertAutofillContentService["insertValueIntoField"](element, value); + + expect(element.innerText).toBe(value); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).toHaveBeenCalledWith(element, expect.any(Function)); + }); + + it("will set the `checked` attribute of any passed checkbox or radio elements", () => { + document.body.innerHTML = ``; + const checkboxElement = document.getElementById("checkbox") as HTMLInputElement; + const radioElement = document.getElementById("radio") as HTMLInputElement; + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + const possibleValues = ["true", "y", "1", "yes", "✓"]; + possibleValues.forEach((value) => { + insertAutofillContentService["insertValueIntoField"](checkboxElement, value); + insertAutofillContentService["insertValueIntoField"](radioElement, value); + + expect(checkboxElement.checked).toBe(true); + expect(radioElement.checked).toBe(true); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).toHaveBeenCalledWith(checkboxElement, expect.any(Function)); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).toHaveBeenCalledWith(radioElement, expect.any(Function)); + + checkboxElement.checked = false; + radioElement.checked = false; + }); + }); + + it("will set the `value` attribute of any passed input or textarea elements", () => { + document.body.innerHTML = ``; + const value1 = "test"; + const value2 = "test2"; + const textInputElement = document.getElementById("username") as HTMLInputElement; + textInputElement.value = value1; + const textareaElement = document.getElementById("bio") as HTMLTextAreaElement; + textareaElement.value = value2; + jest.spyOn(insertAutofillContentService as any, "handleInsertValueAndTriggerSimulatedEvents"); + + insertAutofillContentService["insertValueIntoField"](textInputElement, value1); + + expect(textInputElement.value).toBe(value1); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).toHaveBeenCalledWith(textInputElement, expect.any(Function)); + + insertAutofillContentService["insertValueIntoField"](textareaElement, value2); + + expect(textareaElement.value).toBe(value2); + expect( + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + ).toHaveBeenCalledWith(textareaElement, expect.any(Function)); + }); + }); + + describe("handleInsertValueAndTriggerSimulatedEvents", () => { + it("triggers pre- and post-insert events on the element while filling the value into the element", () => { + const value = "test"; + const element = document.querySelector('input[type="text"]') as FormFieldElement; + jest.spyOn(insertAutofillContentService as any, "triggerPreInsertEventsOnElement"); + jest.spyOn(insertAutofillContentService as any, "triggerPostInsertEventsOnElement"); + jest.spyOn(insertAutofillContentService as any, "triggerFillAnimationOnElement"); + const valueChangeCallback = jest.fn( + () => ((element as FillableFormFieldElement).value = value) + ); + + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"]( + element, + valueChangeCallback + ); + + expect(insertAutofillContentService["triggerPreInsertEventsOnElement"]).toHaveBeenCalledWith( + element + ); + expect(valueChangeCallback).toHaveBeenCalled(); + expect(insertAutofillContentService["triggerPostInsertEventsOnElement"]).toHaveBeenCalledWith( + element + ); + expect(insertAutofillContentService["triggerFillAnimationOnElement"]).toHaveBeenCalledWith( + element + ); + expect((element as FillableFormFieldElement).value).toBe(value); + }); + }); + + describe("triggerPreInsertEventsOnElement", () => { + it("triggers a simulated click and keyboard event on the element", () => { + const initialElementValue = "test"; + document.body.innerHTML = ``; + const element = document.getElementById("username") as FillableFormFieldElement; + jest.spyOn( + insertAutofillContentService as any, + "simulateUserMouseClickAndFocusEventInteractions" + ); + jest.spyOn(insertAutofillContentService as any, "simulateUserKeyboardEventInteractions"); + + insertAutofillContentService["triggerPreInsertEventsOnElement"](element); + + expect( + insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"] + ).toHaveBeenCalledWith(element); + expect( + insertAutofillContentService["simulateUserKeyboardEventInteractions"] + ).toHaveBeenCalledWith(element); + expect(element.value).toBe(initialElementValue); + }); + }); + + describe("triggerPostInsertEventsOnElement", () => { + it("triggers simulated event interactions and blurs the element after", () => { + const elementValue = "test"; + document.body.innerHTML = ``; + const element = document.getElementById("username") as FillableFormFieldElement; + jest.spyOn(element, "blur"); + jest.spyOn(insertAutofillContentService as any, "simulateUserKeyboardEventInteractions"); + jest.spyOn(insertAutofillContentService as any, "simulateInputElementChangedEvent"); + + insertAutofillContentService["triggerPostInsertEventsOnElement"](element); + + expect( + insertAutofillContentService["simulateUserKeyboardEventInteractions"] + ).toHaveBeenCalledWith(element); + expect(insertAutofillContentService["simulateInputElementChangedEvent"]).toHaveBeenCalledWith( + element + ); + expect(element.blur).toHaveBeenCalled(); + expect(element.value).toBe(elementValue); + }); + }); + + describe("triggerFillAnimationOnElement", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllTimers(); + }); + + describe("will not trigger the animation when...", () => { + it("the element is a non-hidden hidden input type", async () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector( + 'input[type="hidden"]' + ) as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + await jest.advanceTimersByTime(200); + + expect(testElement.classList.add).not.toHaveBeenCalled(); + expect(testElement.classList.remove).not.toHaveBeenCalled(); + }); + + it("the element is a non-hidden textarea", () => { + document.body.innerHTML = mockLoginForm + ""; + const testElement = document.querySelector("textarea") as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).not.toHaveBeenCalled(); + expect(testElement.classList.remove).not.toHaveBeenCalled(); + }); + + it("the element is a unsupported tag", () => { + document.body.innerHTML = mockLoginForm + '
    '; + const testElement = document.querySelector("#input-tag") as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).not.toHaveBeenCalled(); + expect(testElement.classList.remove).not.toHaveBeenCalled(); + }); + + it("the element has a `visibility: hidden;` CSS rule applied to it", () => { + const testElement = document.querySelector( + 'input[type="password"]' + ) as FillableFormFieldElement; + testElement.style.visibility = "hidden"; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).not.toHaveBeenCalled(); + expect(testElement.classList.remove).not.toHaveBeenCalled(); + }); + + it("the element has a `display: none;` CSS rule applied to it", () => { + const testElement = document.querySelector( + 'input[type="password"]' + ) as FillableFormFieldElement; + testElement.style.display = "none"; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).not.toHaveBeenCalled(); + expect(testElement.classList.remove).not.toHaveBeenCalled(); + }); + + it("a parent of the element has an `opacity: 0;` CSS rule applied to it", () => { + document.body.innerHTML = + mockLoginForm + '
    '; + const testElement = document.querySelector( + 'input[type="email"]' + ) as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).not.toHaveBeenCalled(); + expect(testElement.classList.remove).not.toHaveBeenCalled(); + }); + }); + + describe("will trigger the animation when...", () => { + it("the element is a non-hidden password field", () => { + const testElement = document.querySelector( + 'input[type="password"]' + ) as FillableFormFieldElement; + jest.spyOn( + insertAutofillContentService["domElementVisibilityService"], + "isElementHiddenByCss" + ); + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect( + insertAutofillContentService["domElementVisibilityService"].isElementHiddenByCss + ).toHaveBeenCalledWith(testElement); + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + + it("the element is a non-hidden email input", () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector( + 'input[type="email"]' + ) as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + + it("the element is a non-hidden text input", () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector( + 'input[type="text"]' + ) as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + + it("the element is a non-hidden number input", () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector( + 'input[type="number"]' + ) as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + + it("the element is a non-hidden tel input", () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector('input[type="tel"]') as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + + it("the element is a non-hidden url input", () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector('input[type="url"]') as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + + it("the element is a non-hidden span", () => { + document.body.innerHTML = mockLoginForm + ''; + const testElement = document.querySelector("#input-tag") as FillableFormFieldElement; + jest.spyOn(testElement.classList, "add"); + jest.spyOn(testElement.classList, "remove"); + + insertAutofillContentService["triggerFillAnimationOnElement"](testElement); + jest.advanceTimersByTime(200); + + expect(testElement.classList.add).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + expect(testElement.classList.remove).toHaveBeenCalledWith( + "com-bitwarden-browser-animated-fill" + ); + }); + }); + }); + + describe("triggerClickOnElement", () => { + it("will trigger a click event on the passed element", () => { + const inputElement = document.querySelector('input[type="text"]') as HTMLElement; + jest.spyOn(inputElement, "click"); + + insertAutofillContentService["triggerClickOnElement"](inputElement); + + expect(inputElement.click).toHaveBeenCalled(); + }); + }); + + describe("triggerFocusOnElement", () => { + it("will trigger a focus event on the passed element and attempt to reset the value", () => { + const value = "test"; + const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement; + inputElement.value = "test"; + jest.spyOn(inputElement, "focus"); + jest.spyOn(window, "String"); + + insertAutofillContentService["triggerFocusOnElement"](inputElement, true); + + expect(window.String).toHaveBeenCalledWith(value); + expect(inputElement.focus).toHaveBeenCalled(); + expect(inputElement.value).toEqual(value); + }); + + it("will not attempt to reset the value but will still focus the element", () => { + const value = "test"; + const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement; + inputElement.value = "test"; + jest.spyOn(inputElement, "focus"); + jest.spyOn(window, "String"); + + insertAutofillContentService["triggerFocusOnElement"](inputElement, false); + + expect(window.String).not.toHaveBeenCalledWith(); + expect(inputElement.focus).toHaveBeenCalled(); + expect(inputElement.value).toEqual(value); + }); + }); + + describe("simulateUserMouseClickAndFocusEventInteractions", () => { + it("will trigger click and focus events on the passed element", () => { + const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement; + jest.spyOn(insertAutofillContentService as any, "triggerClickOnElement"); + jest.spyOn(insertAutofillContentService as any, "triggerFocusOnElement"); + + insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"](inputElement); + + expect(insertAutofillContentService["triggerClickOnElement"]).toHaveBeenCalledWith( + inputElement + ); + expect(insertAutofillContentService["triggerFocusOnElement"]).toHaveBeenCalledWith( + inputElement, + false + ); + }); + }); + + describe("simulateUserKeyboardEventInteractions", () => { + it("will trigger `keydown`, `keypress`, and `keyup` events on the passed element", () => { + const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement; + jest.spyOn(inputElement, "dispatchEvent"); + + insertAutofillContentService["simulateUserKeyboardEventInteractions"](inputElement); + + [EVENTS.KEYDOWN, EVENTS.KEYPRESS, EVENTS.KEYUP].forEach((eventName) => { + expect(inputElement.dispatchEvent).toHaveBeenCalledWith( + new KeyboardEvent(eventName, { bubbles: true }) + ); + }); + }); + }); + + describe("simulateInputElementChangedEvent", () => { + it("will trigger `input` and `change` events on the passed element", () => { + const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement; + jest.spyOn(inputElement, "dispatchEvent"); + + insertAutofillContentService["simulateInputElementChangedEvent"](inputElement); + + [EVENTS.INPUT, EVENTS.CHANGE].forEach((eventName) => { + expect(inputElement.dispatchEvent).toHaveBeenCalledWith( + new Event(eventName, { bubbles: true }) + ); + }); + }); + }); +}); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts new file mode 100644 index 00000000000..89f644ba6be --- /dev/null +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -0,0 +1,349 @@ +import { EVENTS, TYPE_CHECK } from "../constants"; +import AutofillScript, { AutofillInsertActions, FillScript } from "../models/autofill-script"; +import { FormFieldElement } from "../types"; + +import { InsertAutofillContentService as InsertAutofillContentServiceInterface } from "./abstractions/insert-autofill-content.service"; +import CollectAutofillContentService from "./collect-autofill-content.service"; +import DomElementVisibilityService from "./dom-element-visibility.service"; + +class InsertAutofillContentService implements InsertAutofillContentServiceInterface { + private readonly domElementVisibilityService: DomElementVisibilityService; + private readonly collectAutofillContentService: CollectAutofillContentService; + private readonly autofillInsertActions: AutofillInsertActions = { + fill_by_opid: ({ opid, value }) => this.handleFillFieldByOpidAction(opid, value), + click_on_opid: ({ opid }) => this.handleClickOnFieldByOpidAction(opid), + focus_by_opid: ({ opid }) => this.handleFocusOnFieldByOpidAction(opid), + }; + + /** + * InsertAutofillContentService constructor. Instantiates the + * DomElementVisibilityService and CollectAutofillContentService classes. + */ + constructor( + domElementVisibilityService: DomElementVisibilityService, + collectAutofillContentService: CollectAutofillContentService + ) { + this.domElementVisibilityService = domElementVisibilityService; + this.collectAutofillContentService = collectAutofillContentService; + } + + /** + * Handles autofill of the forms on the current page based on the + * data within the passed fill script object. + * @param {AutofillScript} fillScript + * @public + */ + fillForm(fillScript: AutofillScript) { + if ( + !fillScript.script?.length || + this.fillingWithinSandboxedIframe() || + this.userCancelledInsecureUrlAutofill(fillScript.savedUrls) || + this.userCancelledUntrustedIframeAutofill(fillScript) + ) { + return; + } + + fillScript.script.forEach(this.runFillScriptAction); + } + + /** + * Identifies if the execution of this script is happening + * within a sandboxed iframe. + * @returns {boolean} + * @private + */ + private fillingWithinSandboxedIframe() { + return ( + String(self.origin).toLowerCase() === "null" || + window.frameElement?.hasAttribute("sandbox") || + window.location.hostname === "" + ); + } + + /** + * Checks if the autofill is occurring on a page that can be considered secure. If the page is not secure, + * the user is prompted to confirm that they want to autofill on the page. + * @param {string[] | null} savedUrls + * @returns {boolean} + * @private + */ + private userCancelledInsecureUrlAutofill(savedUrls?: string[] | null): boolean { + if ( + !savedUrls?.some((url) => url.startsWith(`https://${window.location.hostname}`)) || + window.location.protocol !== "http:" || + !document.querySelectorAll("input[type=password]")?.length + ) { + return false; + } + + const confirmationWarning = [ + chrome.i18n.getMessage("insecurePageWarning"), + chrome.i18n.getMessage("insecurePageWarningFillPrompt", [window.location.hostname]), + ].join("\n\n"); + + return !confirm(confirmationWarning); + } + + /** + * Checking if the autofill is occurring within an untrusted iframe. If the page is within an untrusted iframe, + * the user is prompted to confirm that they want to autofill on the page. If the user cancels the autofill, + * the script will not continue. + * + * Note: confirm() is blocked by sandboxed iframes, but we don't want to fill sandboxed iframes anyway. + * If this occurs, confirm() returns false without displaying the dialog box, and autofill will be aborted. + * The browser may print a message to the console, but this is not a standard error that we can handle. + * @param {AutofillScript} fillScript + * @returns {boolean} + * @private + */ + private userCancelledUntrustedIframeAutofill(fillScript: AutofillScript): boolean { + if (!fillScript.untrustedIframe) { + return false; + } + + const confirmationWarning = [ + chrome.i18n.getMessage("autofillIframeWarning"), + chrome.i18n.getMessage("autofillIframeWarningTip", [window.location.hostname]), + ].join("\n\n"); + + return !confirm(confirmationWarning); + } + + /** + * Runs the autofill action based on the action type and the opid. + * Each action is subsequently delayed by 20 milliseconds. + * @param {FillScriptActions} action + * @param {string} opid + * @param {string} value + * @param {number} actionIndex + */ + private runFillScriptAction = ([action, opid, value]: FillScript, actionIndex: number): void => { + if (!opid || !this.autofillInsertActions[action]) { + return; + } + + const delayActionsInMilliseconds = 20; + setTimeout( + () => this.autofillInsertActions[action]({ opid, value }), + delayActionsInMilliseconds * actionIndex + ); + }; + + /** + * Queries the DOM for an element by opid and inserts the passed value into the element. + * @param {string} opid + * @param {string} value + * @private + */ + private handleFillFieldByOpidAction(opid: string, value: string) { + const element = this.collectAutofillContentService.getAutofillFieldElementByOpid(opid); + this.insertValueIntoField(element, value); + } + + /** + * Handles finding an element by opid and triggering a click event on the element. + * @param {string} opid + * @private + */ + private handleClickOnFieldByOpidAction(opid: string) { + const element = this.collectAutofillContentService.getAutofillFieldElementByOpid(opid); + this.triggerClickOnElement(element); + } + + /** + * Handles finding an element by opid and triggering click and focus events on the element. + * @param {string} opid + * @private + */ + private handleFocusOnFieldByOpidAction(opid: string) { + const element = this.collectAutofillContentService.getAutofillFieldElementByOpid(opid); + this.simulateUserMouseClickAndFocusEventInteractions(element, true); + } + + /** + * Identifies the type of element passed and inserts the value into the element. + * Will trigger simulated events on the element to ensure that the element is + * properly updated. + * @param {FormFieldElement | null} element + * @param {string} value + * @private + */ + private insertValueIntoField(element: FormFieldElement | null, value: string) { + const elementCanBeReadonly = + element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement; + const elementCanBeFilled = elementCanBeReadonly || element instanceof HTMLSelectElement; + + if ( + !element || + !value || + (elementCanBeReadonly && element.readOnly) || + (elementCanBeFilled && element.disabled) + ) { + return; + } + + if (element instanceof HTMLSpanElement) { + this.handleInsertValueAndTriggerSimulatedEvents(element, () => (element.innerText = value)); + return; + } + + const isFillableCheckboxOrRadioElement = + element instanceof HTMLInputElement && + new Set(["checkbox", "radio"]).has(element.type) && + new Set(["true", "y", "1", "yes", "✓"]).has(String(value).toLowerCase()); + if (isFillableCheckboxOrRadioElement) { + this.handleInsertValueAndTriggerSimulatedEvents(element, () => (element.checked = true)); + return; + } + + this.handleInsertValueAndTriggerSimulatedEvents(element, () => (element.value = value)); + } + + /** + * Simulates pre- and post-insert events on the element meant to mimic user interactions + * while inserting the autofill value into the element. + * @param {FormFieldElement} element + * @param {Function} valueChangeCallback + * @private + */ + private handleInsertValueAndTriggerSimulatedEvents( + element: FormFieldElement, + valueChangeCallback: CallableFunction + ): void { + this.triggerPreInsertEventsOnElement(element); + valueChangeCallback(); + this.triggerPostInsertEventsOnElement(element); + this.triggerFillAnimationOnElement(element); + } + + /** + * Simulates a mouse click event on the element, including focusing the event, and + * the triggers a simulated keyboard event on the element. Will attempt to ensure + * that the initial element value is not arbitrarily changed by the simulated events. + * @param {FormFieldElement} element + * @private + */ + private triggerPreInsertEventsOnElement(element: FormFieldElement): void { + const initialElementValue = "value" in element ? element.value : ""; + + this.simulateUserMouseClickAndFocusEventInteractions(element); + this.simulateUserKeyboardEventInteractions(element); + + if ("value" in element && initialElementValue !== element.value) { + element.value = initialElementValue; + } + } + + /** + * Simulates a keyboard event on the element before assigning the autofilled value to the element, and then + * simulates an input change event on the element to trigger expected events after autofill occurs. + * @param {FormFieldElement} element + * @private + */ + private triggerPostInsertEventsOnElement(element: FormFieldElement): void { + const autofilledValue = "value" in element ? element.value : ""; + this.simulateUserKeyboardEventInteractions(element); + + if ("value" in element && autofilledValue !== element.value) { + element.value = autofilledValue; + } + + this.simulateInputElementChangedEvent(element); + element.blur(); + } + + /** + * Identifies if a passed element can be animated and sets a class on the element + * to trigger a CSS animation. The animation is removed after a short delay. + * @param {FormFieldElement} element + * @private + */ + private triggerFillAnimationOnElement(element: FormFieldElement): void { + const skipAnimatingElement = + !(element instanceof HTMLSpanElement) && + !new Set(["email", "text", "password", "number", "tel", "url"]).has(element?.type); + + if (this.domElementVisibilityService.isElementHiddenByCss(element) || skipAnimatingElement) { + return; + } + + element.classList.add("com-bitwarden-browser-animated-fill"); + setTimeout(() => element.classList.remove("com-bitwarden-browser-animated-fill"), 200); + } + + /** + * Simulates a click event on the element. + * @param {HTMLElement} element + * @private + */ + private triggerClickOnElement(element?: HTMLElement): void { + if (typeof element?.click !== TYPE_CHECK.FUNCTION) { + return; + } + + element.click(); + } + + /** + * Simulates a focus event on the element. Will optionally reset the value of the element + * if the element has a value property. + * @param {HTMLElement | undefined} element + * @param {boolean} shouldResetValue + * @private + */ + private triggerFocusOnElement(element: HTMLElement | undefined, shouldResetValue = false): void { + if (typeof element?.focus !== TYPE_CHECK.FUNCTION) { + return; + } + + let initialValue = ""; + if (shouldResetValue && "value" in element) { + initialValue = String(element.value); + } + + element.focus(); + + if (initialValue && "value" in element) { + element.value = initialValue; + } + } + + /** + * Simulates a mouse click and focus event on the element. + * @param {FormFieldElement} element + * @param {boolean} shouldResetValue + * @private + */ + private simulateUserMouseClickAndFocusEventInteractions( + element: FormFieldElement, + shouldResetValue = false + ): void { + this.triggerClickOnElement(element); + this.triggerFocusOnElement(element, shouldResetValue); + } + + /** + * Simulates several keyboard events on the element, mocking a user interaction with the element. + * @param {FormFieldElement} element + * @private + */ + private simulateUserKeyboardEventInteractions(element: FormFieldElement): void { + [EVENTS.KEYDOWN, EVENTS.KEYPRESS, EVENTS.KEYUP].forEach((eventType) => + element.dispatchEvent(new KeyboardEvent(eventType, { bubbles: true })) + ); + } + + /** + * Simulates an input change event on the element, mocking behavior that would occur if a user + * manually changed a value for the element. + * @param {FormFieldElement} element + * @private + */ + private simulateInputElementChangedEvent(element: FormFieldElement): void { + [EVENTS.INPUT, EVENTS.CHANGE].forEach((eventType) => + element.dispatchEvent(new Event(eventType, { bubbles: true })) + ); + } +} + +export default InsertAutofillContentService; diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts index d6891325353..8bab87709d2 100644 --- a/apps/browser/src/autofill/types/index.ts +++ b/apps/browser/src/autofill/types/index.ts @@ -39,3 +39,22 @@ export type UserSettings = { vaultTimeout: number; vaultTimeoutAction: VaultTimeoutAction; }; + +/** + * A HTMLElement (usually a form element) with additional custom properties added by this script + */ +export type ElementWithOpId = T & { + opid: string; +}; + +/** + * A Form Element that we can set a value on (fill) + */ +export type FillableFormFieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; + +/** + * The autofill script's definition of a Form Element (only a subset of HTML form elements) + */ +export type FormFieldElement = FillableFormFieldElement | HTMLSpanElement; + +export type FormElementWithAttribute = FormFieldElement & Record; diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 31e81c198f6..f9963bcf7da 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -52,6 +52,7 @@ import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/pla import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; +import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; @@ -532,6 +533,7 @@ export default class MainBackground { this.authService, this.messagingService ); + this.configApiService = new ConfigApiService(this.apiService, this.authService); this.configService = new ConfigService( this.stateService, this.configApiService, diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index ee15c0a3b9c..c1cfdf0420f 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -1,4 +1,5 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -135,6 +136,12 @@ export default class RuntimeBackground { BrowserApi.closeBitwardenExtensionTab(); }, msg.delay ?? 0); break; + case "triggerAutofillScriptInjection": + await this.autofillService.injectAutofillScripts( + sender, + await this.configService.getFeatureFlagBool(FeatureFlag.AutofillV2) + ); + break; case "bgCollectPageDetails": await this.main.collectPageDetailsForContentScript(sender.tab, msg.sender, sender.frameId); break; diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 1e7b3a3139f..b6276b434e3 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -17,12 +17,7 @@ "content_scripts": [ { "all_frames": true, - "js": [ - "content/autofill.js", - "content/autofiller.js", - "content/notificationBar.js", - "content/contextMenuHandler.js" - ], + "js": ["content/trigger-autofill-script-injection.js"], "matches": ["http://*/*", "https://*/*", "file:///*"], "run_at": "document_start" }, diff --git a/apps/browser/src/platform/browser/browser-api.spec.ts b/apps/browser/src/platform/browser/browser-api.spec.ts new file mode 100644 index 00000000000..af9e633a7f1 --- /dev/null +++ b/apps/browser/src/platform/browser/browser-api.spec.ts @@ -0,0 +1,56 @@ +import { mock } from "jest-mock-extended"; + +import { BrowserApi } from "./browser-api"; + +describe("BrowserApi", () => { + const executeScriptResult = ["value"]; + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("executeScriptInTab", () => { + it("calls to the extension api to execute a script within the give tabId", async () => { + const tabId = 1; + const injectDetails = mock(); + jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(2); + (chrome.tabs.executeScript as jest.Mock).mockImplementation( + (tabId, injectDetails, callback) => callback(executeScriptResult) + ); + + const result = await BrowserApi.executeScriptInTab(tabId, injectDetails); + + expect(chrome.tabs.executeScript).toHaveBeenCalledWith( + tabId, + injectDetails, + expect.any(Function) + ); + expect(result).toEqual(executeScriptResult); + }); + + it("calls the manifest v3 scripting API if the extension manifest is for v3", async () => { + const tabId = 1; + const injectDetails = mock({ + file: "file.js", + allFrames: true, + runAt: "document_start", + frameId: null, + }); + jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(3); + (chrome.scripting.executeScript as jest.Mock).mockResolvedValue(executeScriptResult); + + const result = await BrowserApi.executeScriptInTab(tabId, injectDetails); + + expect(chrome.scripting.executeScript).toHaveBeenCalledWith({ + target: { + tabId: tabId, + allFrames: injectDetails.allFrames, + frameIds: null, + }, + files: [injectDetails.file], + injectImmediately: true, + }); + expect(result).toEqual(executeScriptResult); + }); + }); +}); diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 675fd0b119e..5a5596a795a 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -308,4 +308,31 @@ export class BrowserApi { } return win.opr?.sidebarAction || browser.sidebarAction; } + + /** + * Extension API helper method used to execute a script in a tab. + * @see https://developer.chrome.com/docs/extensions/reference/tabs/#method-executeScript + * @param {number} tabId + * @param {chrome.tabs.InjectDetails} details + * @returns {Promise} + */ + static executeScriptInTab(tabId: number, details: chrome.tabs.InjectDetails) { + if (BrowserApi.manifestVersion === 3) { + return chrome.scripting.executeScript({ + target: { + tabId: tabId, + allFrames: details.allFrames, + frameIds: details.frameId ? [details.frameId] : null, + }, + files: details.file ? [details.file] : null, + injectImmediately: details.runAt === "document_start", + }); + } + + return new Promise((resolve) => { + chrome.tabs.executeScript(tabId, details, (result) => { + resolve(result); + }); + }); + } } diff --git a/apps/browser/test.setup.ts b/apps/browser/test.setup.ts index f87fa9c2c12..6feb163e0a6 100644 --- a/apps/browser/test.setup.ts +++ b/apps/browser/test.setup.ts @@ -30,9 +30,25 @@ const contextMenus = { removeAll: jest.fn(), }; +const i18n = { + getMessage: jest.fn(), +}; + +const tabs = { + executeScript: jest.fn(), + sendMessage: jest.fn(), +}; + +const scripting = { + executeScript: jest.fn(), +}; + // set chrome global.chrome = { + i18n, storage, runtime, contextMenus, + tabs, + scripting, } as any; diff --git a/apps/browser/tsconfig.spec.json b/apps/browser/tsconfig.spec.json index de184bd7608..79b5f5bc4b6 100644 --- a/apps/browser/tsconfig.spec.json +++ b/apps/browser/tsconfig.spec.json @@ -1,4 +1,7 @@ { "extends": "./tsconfig.json", - "files": ["./test.setup.ts"] + "files": ["./test.setup.ts"], + "compilerOptions": { + "esModuleInterop": true + } } diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index 231b9ab1561..23fba305b6e 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -14,10 +14,9 @@ if (process.env.NODE_ENV == null) { } const ENV = (process.env.ENV = process.env.NODE_ENV); const manifestVersion = process.env.MANIFEST_VERSION == 3 ? 3 : 2; -const autofillVersion = process.env.AUTOFILL_VERSION == 2 ? 2 : 1; console.log(`Building Manifest Version ${manifestVersion} app`); -console.log(`Using Autofill v${autofillVersion}`); + const envConfig = configurator.load(ENV); configurator.log(envConfig); @@ -153,6 +152,10 @@ const mainConfig = { entry: { "popup/polyfills": "./src/popup/polyfills.ts", "popup/main": "./src/popup/main.ts", + "content/trigger-autofill-script-injection": + "./src/autofill/content/trigger-autofill-script-injection.ts", + "content/autofill": "./src/autofill/content/autofill.js", + "content/autofill-init": "./src/autofill/content/autofill-init.ts", "content/autofiller": "./src/autofill/content/autofiller.ts", "content/notificationBar": "./src/autofill/content/notification-bar.ts", "content/contextMenuHandler": "./src/autofill/content/context-menu-handler.ts", @@ -312,12 +315,4 @@ if (manifestVersion == 2) { configs.push(backgroundConfig); } -if (autofillVersion == 2) { - // Typescript refactors (WIP) - mainConfig.entry["content/autofill"] = "./src/autofill/content/autofillv2.ts"; -} else { - // Javascript (used in production) - mainConfig.entry["content/autofill"] = "./src/autofill/content/autofill.js"; -} - module.exports = configs; diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index fb155f54e2d..7016849b3bc 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -2,5 +2,6 @@ export enum FeatureFlag { DisplayEuEnvironmentFlag = "display-eu-environment", DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning", TrustedDeviceEncryption = "trusted-device-encryption", + AutofillV2 = "autofill-v2", SecretsManagerBilling = "sm-ga-billing", } From 5440e372f66f978b6ea830fa9e799ab8ca0f237c Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 7 Sep 2023 14:44:55 -0700 Subject: [PATCH 109/135] [PM-3804] Remove Server Flag Icons (#6207) * remove flags from web component * remove selectedRegionImageName from web component * remove input * delete image files and update browser translation * update translation and popup width for destkop/browser * remove translations * revert width on dialog --- apps/browser/src/_locales/en/messages.json | 4 +-- apps/browser/src/popup/images/flag-eu.svg | 28 ------------------ apps/browser/src/popup/images/flag-us.svg | 9 ------ apps/browser/src/popup/scss/environment.scss | 13 --------- .../src/popup/settings/about.component.html | 2 +- apps/desktop/src/images/flag-eu.svg | 28 ------------------ apps/desktop/src/images/flag-us.svg | 9 ------ apps/desktop/src/locales/en/messages.json | 4 +-- apps/desktop/src/scss/environment.scss | 13 --------- .../trial-initiation.component.html | 1 - .../environment-selector.component.html | 29 +------------------ .../environment-selector.component.ts | 13 +-------- .../layouts/frontend-layout.component.html | 2 +- apps/web/src/images/flag-eu.svg | 28 ------------------ apps/web/src/images/flag-us.svg | 9 ------ apps/web/src/locales/en/messages.json | 6 ---- .../environment-selector.component.html | 11 ++----- 17 files changed, 10 insertions(+), 199 deletions(-) delete mode 100644 apps/browser/src/popup/images/flag-eu.svg delete mode 100644 apps/browser/src/popup/images/flag-us.svg delete mode 100644 apps/desktop/src/images/flag-eu.svg delete mode 100644 apps/desktop/src/images/flag-us.svg delete mode 100644 apps/web/src/images/flag-eu.svg delete mode 100644 apps/web/src/images/flag-us.svg diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/popup/images/flag-eu.svg b/apps/browser/src/popup/images/flag-eu.svg deleted file mode 100644 index bbfefd6b47a..00000000000 --- a/apps/browser/src/popup/images/flag-eu.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/browser/src/popup/images/flag-us.svg b/apps/browser/src/popup/images/flag-us.svg deleted file mode 100644 index 615946d4b59..00000000000 --- a/apps/browser/src/popup/images/flag-us.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/browser/src/popup/scss/environment.scss b/apps/browser/src/popup/scss/environment.scss index 19d39655f2a..1ac0f4240bd 100644 --- a/apps/browser/src/popup/scss/environment.scss +++ b/apps/browser/src/popup/scss/environment.scss @@ -90,19 +90,6 @@ html.browser_safari { background-color: themed("listItemBackgroundHoverColor") !important; } } - - img { - margin-bottom: -2px; - height: 14px; - } - - .img-us { - content: url("../images/flag-us.svg"); - } - - .img-eu { - content: url("../images/flag-eu.svg"); - } } .environment-selector-padding { diff --git a/apps/browser/src/popup/settings/about.component.html b/apps/browser/src/popup/settings/about.component.html index c8840833cf7..24fea4eb9da 100644 --- a/apps/browser/src/popup/settings/about.component.html +++ b/apps/browser/src/popup/settings/about.component.html @@ -33,7 +33,7 @@

    - {{ "serverVersion" | i18n }} ({{ "selfHosted" | i18n }}): + {{ "serverVersion" | i18n }} ({{ "selfHostedServer" | i18n }}): {{ this.serverConfig?.version }} ({{ "lastSeenOn" | i18n : (serverConfig.utcDate | date : "mediumDate") }}) diff --git a/apps/desktop/src/images/flag-eu.svg b/apps/desktop/src/images/flag-eu.svg deleted file mode 100644 index bbfefd6b47a..00000000000 --- a/apps/desktop/src/images/flag-eu.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/desktop/src/images/flag-us.svg b/apps/desktop/src/images/flag-us.svg deleted file mode 100644 index 615946d4b59..00000000000 --- a/apps/desktop/src/images/flag-us.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index dbcb70d5e0c..b032c6d2de3 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/scss/environment.scss b/apps/desktop/src/scss/environment.scss index b9e3eaa7b23..b18032f1a71 100644 --- a/apps/desktop/src/scss/environment.scss +++ b/apps/desktop/src/scss/environment.scss @@ -83,17 +83,4 @@ background-color: themed("listItemBackgroundHoverColor") !important; } } - - img { - margin-bottom: -2px; - height: 14px; - } - - .img-us { - content: url("../images/flag-us.svg"); - } - - .img-eu { - content: url("../images/flag-eu.svg"); - } } diff --git a/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html b/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html index b8127d00c1c..af8c255f632 100644 --- a/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html +++ b/apps/web/src/app/auth/trial-initiation/trial-initiation.component.html @@ -63,7 +63,6 @@ {{ "startYour7DayFreeTrialOfBitwardenFor" | i18n : org }}

    diff --git a/apps/web/src/app/components/environment-selector/environment-selector.component.html b/apps/web/src/app/components/environment-selector/environment-selector.component.html index 5a5bada82df..d17a9c2b43c 100644 --- a/apps/web/src/app/components/environment-selector/environment-selector.component.html +++ b/apps/web/src/app/components/environment-selector/environment-selector.component.html @@ -12,11 +12,6 @@ aria-hidden="true" [style.visibility]="isUsServer ? 'visible' : 'hidden'" > - {{ 'usFlag' | i18n }} {{ "usDomain" | i18n }}
    - - - - {{ 'selectedRegionFlag' | i18n }} - - -
    +
    {{ "server" | i18n }}: {{ isEuServer ? ("euDomain" | i18n) : ("usDomain" | i18n) }}
    - + © {{ year }} Bitwarden Inc.
    {{ "versionNumber" | i18n : version }}
    diff --git a/apps/web/src/images/flag-eu.svg b/apps/web/src/images/flag-eu.svg deleted file mode 100644 index bbfefd6b47a..00000000000 --- a/apps/web/src/images/flag-eu.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/web/src/images/flag-us.svg b/apps/web/src/images/flag-us.svg deleted file mode 100644 index 615946d4b59..00000000000 --- a/apps/web/src/images/flag-us.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index dee5121acd0..43ef1799ea2 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7019,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/libs/angular/src/auth/components/environment-selector.component.html b/libs/angular/src/auth/components/environment-selector.component.html index c7307edce90..4e8d33bde68 100644 --- a/libs/angular/src/auth/components/environment-selector.component.html +++ b/libs/angular/src/auth/components/environment-selector.component.html @@ -15,7 +15,7 @@ "euDomain" | i18n }}
    @@ -44,7 +44,6 @@ selectedEnvironment === ServerEnvironmentType.US ? 'visible' : 'hidden' " > - {{ "usDomain" | i18n }}
    @@ -62,7 +61,6 @@ selectedEnvironment === ServerEnvironmentType.EU ? 'visible' : 'hidden' " >
    - {{ "euDomain" | i18n }}
    @@ -79,12 +77,7 @@ selectedEnvironment === ServerEnvironmentType.SelfHosted ? 'visible' : 'hidden' " > - - {{ "selfHosted" | i18n }} + {{ "selfHostedServer" | i18n }}
    From a21892103aa92008ad242861967a281a9741027a Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:51:16 -0500 Subject: [PATCH 110/135] Display max project error on import (#6211) --- .../settings/porting/sm-import.component.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts index 3542ee7b8ee..3819b7f1dbe 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts @@ -8,6 +8,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; import { DialogService } from "@bitwarden/components"; import { @@ -73,6 +74,13 @@ export class SecretsManagerImportComponent implements OnInit, OnDestroy { if (error?.lines?.length > 0) { this.openImportErrorDialog(error); return; + } else if (!Utils.isNullOrWhitespace(error?.message)) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + error.message + ); + return; } else if (error != null) { this.platformUtilsService.showToast( "error", From 1c0037993140964b9e50ab1d37facefe2a49f3c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:36:54 +0000 Subject: [PATCH 111/135] Autosync the updated translations (#6229) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 4 +- apps/desktop/src/locales/ar/messages.json | 4 +- apps/desktop/src/locales/az/messages.json | 6 +- apps/desktop/src/locales/be/messages.json | 82 +- apps/desktop/src/locales/bg/messages.json | 16 +- apps/desktop/src/locales/bn/messages.json | 4 +- apps/desktop/src/locales/bs/messages.json | 4 +- apps/desktop/src/locales/ca/messages.json | 84 +- apps/desktop/src/locales/cs/messages.json | 6 +- apps/desktop/src/locales/cy/messages.json | 4 +- apps/desktop/src/locales/da/messages.json | 4 +- apps/desktop/src/locales/de/messages.json | 4 +- apps/desktop/src/locales/el/messages.json | 4 +- apps/desktop/src/locales/en_GB/messages.json | 4 +- apps/desktop/src/locales/en_IN/messages.json | 4 +- apps/desktop/src/locales/eo/messages.json | 4 +- apps/desktop/src/locales/es/messages.json | 4 +- apps/desktop/src/locales/et/messages.json | 4 +- apps/desktop/src/locales/eu/messages.json | 4 +- apps/desktop/src/locales/fa/messages.json | 4 +- apps/desktop/src/locales/fi/messages.json | 12 +- apps/desktop/src/locales/fil/messages.json | 4 +- apps/desktop/src/locales/fr/messages.json | 78 +- apps/desktop/src/locales/gl/messages.json | 4 +- apps/desktop/src/locales/he/messages.json | 6 +- apps/desktop/src/locales/hi/messages.json | 4 +- apps/desktop/src/locales/hr/messages.json | 4 +- apps/desktop/src/locales/hu/messages.json | 6 +- apps/desktop/src/locales/id/messages.json | 4 +- apps/desktop/src/locales/it/messages.json | 6 +- apps/desktop/src/locales/ja/messages.json | 4 +- apps/desktop/src/locales/ka/messages.json | 4 +- apps/desktop/src/locales/km/messages.json | 4 +- apps/desktop/src/locales/kn/messages.json | 4 +- apps/desktop/src/locales/ko/messages.json | 4 +- apps/desktop/src/locales/lt/messages.json | 938 +++++++++---------- apps/desktop/src/locales/lv/messages.json | 6 +- apps/desktop/src/locales/me/messages.json | 4 +- apps/desktop/src/locales/ml/messages.json | 4 +- apps/desktop/src/locales/mr/messages.json | 4 +- apps/desktop/src/locales/my/messages.json | 4 +- apps/desktop/src/locales/nb/messages.json | 4 +- apps/desktop/src/locales/ne/messages.json | 20 +- apps/desktop/src/locales/nl/messages.json | 4 +- apps/desktop/src/locales/nn/messages.json | 4 +- apps/desktop/src/locales/or/messages.json | 4 +- apps/desktop/src/locales/pl/messages.json | 4 +- apps/desktop/src/locales/pt_BR/messages.json | 4 +- apps/desktop/src/locales/pt_PT/messages.json | 6 +- apps/desktop/src/locales/ro/messages.json | 4 +- apps/desktop/src/locales/ru/messages.json | 12 +- apps/desktop/src/locales/si/messages.json | 4 +- apps/desktop/src/locales/sk/messages.json | 6 +- apps/desktop/src/locales/sl/messages.json | 4 +- apps/desktop/src/locales/sr/messages.json | 6 +- apps/desktop/src/locales/sv/messages.json | 4 +- apps/desktop/src/locales/te/messages.json | 4 +- apps/desktop/src/locales/th/messages.json | 4 +- apps/desktop/src/locales/tr/messages.json | 4 +- apps/desktop/src/locales/uk/messages.json | 10 +- apps/desktop/src/locales/vi/messages.json | 4 +- apps/desktop/src/locales/zh_CN/messages.json | 10 +- apps/desktop/src/locales/zh_TW/messages.json | 4 +- 63 files changed, 746 insertions(+), 746 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index fa880624311..7ef63a8aab2 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Selghehuisves" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Toegang geweier. U het nie toestemming om hierdie blad te sien nie." diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 914d2026267..ceefd1ecc26 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "استضافة ذاتية" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "غير مسموح بالدخول. ليس لديك الصلاحية لعرض هذه الصفحة." diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 3256e1198b2..8d0f0211a4c 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1078,7 +1078,7 @@ "message": "Fayl qoşmaları üçün 1 GB şifrələnmiş saxlama sahəsi." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "YubiKey və Duo kimi mülkiyyətçi iki addımlı giriş seçimləri." }, "premiumSignUpReports": { "message": "Anbarınızın təhlükəsiyini təmin etmək üçün parol gigiyenası, hesab sağlamlığı və verilənlərin pozulması hesabatları." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Öz-özünə sahiblik edən" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Müraciət rədd edildi. Bu səhifəyə baxmaq üçün icazəniz yoxdur." diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index fc45db18b3f..666dac70c24 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -1078,7 +1078,7 @@ "message": "1 ГБ зашыфраванага сховішча для далучаных файлаў." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Прапрыетарныя варыянты двухэтапнага ўваходу, такія як YubiKey, FIDO U2F і Duo." }, "premiumSignUpReports": { "message": "Гігіена пароляў, здароўе ўліковага запісу і справаздачы аб уцечках даных для забеспячэння бяспекі вашага сховішча." @@ -1493,7 +1493,7 @@ "message": "Для таго, каб аднавіць доступ да сховішча, патрабуецца паўторная аўтарызацыя." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Наладзіць метад разблакіроўкі для змянення дзеяння часу чакання вашага сховішча." }, "lock": { "message": "Заблакіраваць", @@ -2110,7 +2110,7 @@ "message": "Увайсці з іншай прылады" }, "loginInitiated": { - "message": "Login initiated" + "message": "Ініцыяваны ўваход" }, "notificationSentDevice": { "message": "Апавяшчэнне было адпраўлена на вашу прыладу." @@ -2250,31 +2250,31 @@ "message": "Рэкамендаваныя налады абнаўлення" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Патрабуецца ўхваленне прылады. Выберыце параметры ўхвалення ніжэй:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Запомніць гэту прыладу" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Здыміце пазнаку, калі выкарыстоўваеце агульнадаступную прыладу" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Ухваліць з іншай вашай прылады" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Запытаць ухваленне адміністратара" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Ухваліць з дапамогай асноўнага пароля" }, "region": { - "message": "Region" + "message": "Рэгіён" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Неабходны ідэнтыфікатар SSO арганізацыі." }, "eu": { - "message": "EU", + "message": "ЕС", "description": "European Union" }, "loggingInOn": { @@ -2286,47 +2286,47 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Уласнае размяшчэнне" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Доступ забаронены. У вас не дастаткова правоў для прагляду гэтай старонкі." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Уліковы запіс паспяхова створаны!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Патрабуецца ўхваленне адміністратара" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Ваш запыт адпраўлены адміністратару." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Вы атрымаеце апавяшчэння пасля яго ўхвалення." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Праблемы з уваходам?" }, "loginApproved": { - "message": "Login approved" + "message": "Уваход ухвалены" }, "userEmailMissing": { - "message": "User email missing" + "message": "Адсутнічае электронная пошта карыстальніка" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Давераная прылада" }, "inputRequired": { - "message": "Input is required." + "message": "Неабходны ўвод даных." }, "required": { - "message": "required" + "message": "патрабуецца" }, "search": { - "message": "Search" + "message": "Пошук" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Даўжыня ўведзеных даных павінна складаць прынамсі $COUNT$ сімв.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Даўжыня ўведзеных даных не можа перавышаць наступную колькасць сімвалаў: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Наступныя сімвалы забаронены: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Мінімальная даўжыня сімвалаў значэння, якое будзе ўводзіцца: $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Максімальная даўжыня сімвалаў значэння, якое будзе ўводзіцца: $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 або некалькі адрасоў электроннай пошты з'яўляюцца памылковымі" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Уведзенае значэнне не павінна змяшчаць толькі прабелы.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Уведзеныя даныя не з'яўляюцца адрасам электроннай пошты." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "Колькасць палёў, якія патрабуюць вашай увагі: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Выбраць --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "- Увядзіце для фільтрацыі -" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Атрыманне параметраў..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Элементаў не знойдзена" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Ачысціць усё" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ яшчэ $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "Падменю" } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 2283ccbde6e..bd6552824a5 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -542,7 +542,7 @@ "message": "Повторното въвеждане на главната парола е задължително." }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "Главната парола трябва да е дълга поне $VALUE$ знака.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -1078,7 +1078,7 @@ "message": "1 ГБ пространство за файлове, които се шифроват." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Частно двустепенно удостоверяване чрез YubiKey и Duo." }, "premiumSignUpReports": { "message": "Проверки в списъците с публикувани пароли, проверка на регистрациите и доклади за пробивите в сигурността, което спомага трезорът ви да е допълнително защитен." @@ -2214,16 +2214,16 @@ "message": "Направена е заявка за вписване" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "Разобличена главна парола" }, "exposedMasterPasswordDesc": { - "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + "message": "Паролата е намерена в пробив на данни. Използвайте уникална парола, за да защитите вашия акаунт. Наистина ли искате да използвате слаба парола?" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "Слаба и разобличена главна парола" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + "message": "Разпозната е слаба парола. Използвайте силна парола, за да защитете данните си. Наистина ли искате да използвате слаба парола?" }, "checkForBreaches": { "message": "Проверяване в известните случаи на изтекли данни за тази парола" @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Собствен хостинг" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Достъпът е отказан. Нямате право за преглед на тази страница." diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index e59dd9de8b9..0297dfc157c 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index c88f7f392f1..0d5ed75caf9 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 9850e4b82a9..ab40caa8431 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opcions propietàries de doble factor com ara YubiKey i Duo." }, "premiumSignUpReports": { "message": "Requisits d'higiene de la contrasenya, salut del compte i informe d'infraccions de dades per mantenir la seguretat de la vostra caixa forta." @@ -1493,7 +1493,7 @@ "message": "Una caixa forta desconnectada requereix que torneu a autentificar-vos per accedir-hi de nou." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Configura un mètode de desbloqueig per canviar l'acció del temps d'espera de la caixa forta." }, "lock": { "message": "Bloqueja", @@ -2110,7 +2110,7 @@ "message": "Inicia sessió amb un altre dispositiu" }, "loginInitiated": { - "message": "Login initiated" + "message": "S'ha iniciat la sessió" }, "notificationSentDevice": { "message": "S'ha enviat una notificació al vostre dispositiu." @@ -2250,35 +2250,35 @@ "message": "Actualització de configuració recomanada" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Cal l'aprovació del dispositiu. Seleccioneu una opció d'aprovació a continuació:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Recorda aquest dispositiu" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Desmarqueu si utilitzeu un dispositiu públic" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Aproveu des d'un altre dispositiu vostre" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Sol·liciteu l'aprovació de l'administrador" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Aprova amb contrasenya mestra" }, "region": { - "message": "Region" + "message": "Regió" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Es requereix un identificador SSO de l'organització." }, "eu": { - "message": "EU", + "message": "UE", "description": "European Union" }, "loggingInOn": { - "message": "Logging in on" + "message": "Inici de sessió en" }, "usDomain": { "message": "bitwarden.com" @@ -2286,47 +2286,47 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Autoallotjat" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Accés denegat. No teniu permís per veure aquesta pàgina." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Compte creat correctament!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "S'ha sol·licitat l'aprovació de l'administrador" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "La vostra sol·licitud s'ha enviat a l'administrador." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Se us notificarà una vegada aprovat." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Teniu problemes per iniciar la sessió?" }, "loginApproved": { - "message": "Login approved" + "message": "S'ha aprovat l'inici de sessió" }, "userEmailMissing": { - "message": "User email missing" + "message": "Falta el correu electrònic de l'usuari" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Dispositiu de confiança" }, "inputRequired": { - "message": "Input is required." + "message": "L'entrada és obligatòria." }, "required": { - "message": "required" + "message": "obligatori" }, "search": { - "message": "Search" + "message": "Cerca" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "L'entrada ha de tenir com a mínim $COUNT$ caràcters.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "L'entrada no ha de superar $COUNT$ caràcters de longitud.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Els següents caràcters no estan permesos:\n$CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "El valor d'entrada ha de ser com a mínim $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "El valor d'entrada no ha de ser superior a $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 o més correus no són vàlids" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "L'entrada no ha de contenir només espais en blanc.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "L'entrada no és una adreça de correu electrònic." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ camp(s) de dalt necessiten la vostra atenció.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Selecciona --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Escriviu per filtrar --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Obtenint opcions..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "No s'ha trobat cap element" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Esborra-ho tot" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ més", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "Submenú" } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 4ce336733f6..3d90680ae0e 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB šifrovaného uložiště pro přílohy." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Volby proprietálních dvoufázových přihlášení jako je YubiKey a Duo." }, "premiumSignUpReports": { "message": "Reporty o hygieně Vašich hesel, zdraví účtu a narušeních bezpečnosti." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Vlastní hosting" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Přístup byl odepřen. Nemáte oprávnění k zobrazení této stránky." diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 38e81a83bfd..6d569a89554 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 157e1313ff5..0f3a5603b7e 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Selv-hostet" + "selfHostedServer": { + "message": "selv-hostet" }, "accessDenied": { "message": "Adgang nægtet. Nødvendig tilladelse til at se siden mangler." diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index d16eb636284..889446eff62 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Selbst gehostet" + "selfHostedServer": { + "message": "selbst gehostet" }, "accessDenied": { "message": "Zugriff verweigert. Du hast keine Berechtigung, diese Seite anzuzeigen." diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index fd800e8d713..9e2c135b873 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 6bc9772eb88..923a8e143b9 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 363648da567..19c80a97d3b 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 66195d90739..0da8a023615 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index b40c409d1b0..aaaa54a35a4 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Autoalojado" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Acceso denegado. No tiene permiso para ver esta página." diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index fb74084186c..603f695d5b3 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 4c3931490df..517b56a07f8 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 9e5f6f8468c..bf7f8b25329 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "خود میزبان" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "دسترسی رد شد. شما اجازه مشاهده این صفحه را ندارید." diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 9025306a708..76bd8d322d4 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -1078,7 +1078,7 @@ "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Omisteiset kaksivaiheisen kirjautumisen vaihtoehdot, kuten YubiKey ja Duo." }, "premiumSignUpReports": { "message": "Salasanahygienian, tilin terveyden ja tietovuotojen raportointitoiminnot pitävät holvisi turvassa." @@ -2256,7 +2256,7 @@ "message": "Muista tämä laite" }, "uncheckIfPublicDevice": { - "message": "Poista käytöstä julkisilla laitteilla" + "message": "Poista valinta julkisilla laitteilla" }, "approveFromYourOtherDevice": { "message": "Hyväksy muilta laitteiltasi" @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Itse ylläpidetty" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Pääsy estetty. Sinulla ei ole oikeutta avata sivua." @@ -2299,7 +2299,7 @@ "message": "Hyväksyntää pyydetty ylläpidolta" }, "adminApprovalRequestSentToAdmins": { - "message": "Pyyntösi on välitetty ylläpidolle." + "message": "Pyyntösi on välitetty ylläpidollesi." }, "youWillBeNotifiedOnceApproved": { "message": "Saat ilmoituksen kun se on hyväksytty." @@ -2308,7 +2308,7 @@ "message": "Ongelmia kirjautumisessa?" }, "loginApproved": { - "message": "Kirjautuminen hyväksyttiin" + "message": "Kirjautuminen hyväksytty" }, "userEmailMissing": { "message": "Käyttäjän sähköpostiosoite puuttuu" diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 87b9bde0c46..c89830c0d8f 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 3f249000bd5..776e518fea8 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -1493,7 +1493,7 @@ "message": "Une nouvelle authentification est requise pour accéder à nouveau à votre coffre." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Configurez une méthode de déverrouillage pour changer le délai d'attente de votre coffre." }, "lock": { "message": "Verrouiller", @@ -2110,7 +2110,7 @@ "message": "Connectez-vous avec un autre appareil" }, "loginInitiated": { - "message": "Login initiated" + "message": "Connexion initiée" }, "notificationSentDevice": { "message": "Une notification a été envoyée à votre appareil." @@ -2250,28 +2250,28 @@ "message": "Une mise à jour des paramètres est recommandée" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "L'approbation de l'appareil est requise. Sélectionnez une option d'approbation ci-dessous:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Se souvenir de cet appareil" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Décocher si vous utilisez un appareil public" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Approuver sur votre autre appareil" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Demander l'approbation de l'administrateur" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Approuver avec le mot de passe principal" }, "region": { - "message": "Region" + "message": "Région" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Identifiant SSO de l'organisation requis." }, "eu": { "message": "EU", @@ -2286,47 +2286,47 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Auto-hébergé" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Accès refusé. Vous n'avez pas l'autorisation de voir cette page." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Compte créé avec succès !" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Approbation d'un admin demandée" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Votre demande a été envoyée à votre administrateur." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Vous serez notifié une fois approuvé." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Problème pour vous connecter ?" }, "loginApproved": { - "message": "Login approved" + "message": "Identifiant approuvée" }, "userEmailMissing": { - "message": "User email missing" + "message": "E-mail de l'utilisateur manquant" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Appareil de confiance" }, "inputRequired": { - "message": "Input is required." + "message": "Saisie requise." }, "required": { - "message": "required" + "message": "requis" }, "search": { - "message": "Search" + "message": "Rechercher" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "La saisie doit comporter au moins $COUNT$ caractères.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "La saisie ne doit pas dépasser $COUNT$ caractères de long.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Les caractères suivants ne sont pas autorisés: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "La valeur d'entrée doit être au moins de $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "La valeur d'entrée ne doit pas excéder $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "Un ou plusieurs adresses e-mail ne sont pas valides" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "La saisie ne doit pas contenir que des espaces.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "La saisie n'est pas une adresse e-mail." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ champ(s) ci-dessus nécessitent votre attention.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Sélectionner--" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Tapez pour Filtrer --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Récupération des options..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Aucun élément trouvé" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Effacer tout" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ de plus", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "Sous-menu" } } diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 38e81a83bfd..6d569a89554 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index ad625991fa1..6fd77738b06 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -1078,7 +1078,7 @@ "message": "1 ג'יגה של מקום אחסון מוצפן עבור קבצים מצורפים." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "אפשרויות כניסה דו שלבית קנייניות כמו YubiKey ו־Duo." }, "premiumSignUpReports": { "message": "היגיינת סיסמאות, מצב בריאות החשבון, ודיווחים מעודכנים על פרצות חדשות בכדי לשמור על הכספת שלך בטוחה." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 156a431a2d2..b19b9abd72e 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 42966a51575..ab08f7d5de9 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 796da6fdece..7df69e64b83 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB titkosított fájlmelléklet tárhely." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Saját kétlépcsős bejelentkezési lehetőségek mint a YubiKey és a Duo." }, "premiumSignUpReports": { "message": "Jelszó higiénia, felhasználói fiók biztonsága, és adatszivárgási jelentések a széf biztonsága érdekében." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Saját kiszolgáló" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "A hozzáférés megtagadásra került. Nincs jogosultság az oldal megtekintésére." diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index b976d1b8fd9..5dca15ddee2 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index f294888cc38..3b4c20e0f00 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB di spazio di archiviazione criptato per gli allegati." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opzioni di verifica in due passaggi proprietarie come YubiKey e Duo." }, "premiumSignUpReports": { "message": "Sicurezza delle password, integrità dell'account e rapporti sulle violazioni dei dati per mantenere la tua cassaforte sicura." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Accesso negato. Non hai i permessi necessari per visualizzare questa pagina." diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index f8c11aca7e5..a4f830256fa 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "自己ホスト型" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "アクセスが拒否されました。このページを表示する権限がありません。" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 38e81a83bfd..6d569a89554 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 38e81a83bfd..6d569a89554 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index bfc29e570a1..5ba16861ece 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 7bff705c8ec..afe34afb3f3 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 38e81a83bfd..c15d273916d 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -3,52 +3,52 @@ "message": "Bitwarden" }, "filters": { - "message": "Filters" + "message": "Filtrai" }, "allItems": { - "message": "All items" + "message": "Visi elementai" }, "favorites": { - "message": "Favorites" + "message": "Mėgstamiausi" }, "types": { - "message": "Types" + "message": "Tipai" }, "typeLogin": { - "message": "Login" + "message": "Prisijungti" }, "typeCard": { - "message": "Card" + "message": "Kortelė" }, "typeIdentity": { - "message": "Identity" + "message": "Tapatybė" }, "typeSecureNote": { - "message": "Secure note" + "message": "Saugus įrašas" }, "folders": { - "message": "Folders" + "message": "Aplankai" }, "collections": { - "message": "Collections" + "message": "Rinkiniai" }, "searchVault": { - "message": "Search vault" + "message": "Ieškoti saugykloje" }, "addItem": { - "message": "Add item" + "message": "Pridėti elementą" }, "shared": { - "message": "Shared" + "message": "Pasidalinta" }, "share": { - "message": "Share" + "message": "Bendrinti" }, "moveToOrganization": { - "message": "Move to organization" + "message": "Perkelti į organizaciją" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ perkelta(s) į $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -61,16 +61,16 @@ } }, "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." + "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." }, "attachments": { - "message": "Attachments" + "message": "Priedai" }, "viewItem": { - "message": "View item" + "message": "Peržiūrėti elementą" }, "name": { - "message": "Name" + "message": "Pavadinimas" }, "uri": { "message": "URI" @@ -86,463 +86,463 @@ } }, "newUri": { - "message": "New URI" + "message": "Naujas URI" }, "username": { - "message": "Username" + "message": "Vartotojo vardas" }, "password": { - "message": "Password" + "message": "Slaptažodis" }, "passphrase": { - "message": "Passphrase" + "message": "Slaptafrazė" }, "editItem": { - "message": "Edit item" + "message": "Redaguoti elementą" }, "emailAddress": { - "message": "Email address" + "message": "El. pašto adresas" }, "verificationCodeTotp": { - "message": "Verification code (TOTP)" + "message": "Patvirtinimo kodas (TOTP)" }, "website": { - "message": "Website" + "message": "Tinklapis" }, "notes": { - "message": "Notes" + "message": "Pastabos" }, "customFields": { - "message": "Custom fields" + "message": "Papildomi laukai" }, "launch": { - "message": "Launch" + "message": "Paleisti" }, "copyValue": { - "message": "Copy value", + "message": "Kopijuoti reikšmę", "description": "Copy value to clipboard" }, "minimizeOnCopyToClipboard": { - "message": "Minimize when copying to clipboard" + "message": "Sumažinti kopijuojant į iškarpinę" }, "minimizeOnCopyToClipboardDesc": { - "message": "Minimize application when copying an item's data to the clipboard." + "message": "Sumažinti aplikaciją kopijuojant elemento duomenis į iškarpinę." }, "toggleVisibility": { - "message": "Toggle visibility" + "message": "Perjungti matomumą" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Perjungti sulankstymą", "description": "Toggling an expand/collapse state." }, "cardholderName": { - "message": "Cardholder name" + "message": "Kortelės savininko vardas" }, "number": { - "message": "Number" + "message": "Numeris" }, "brand": { - "message": "Brand" + "message": "Prekės ženklas" }, "expiration": { - "message": "Expiration" + "message": "Galiojimo pabaiga" }, "securityCode": { - "message": "Security code" + "message": "Apsaugos kodas" }, "identityName": { - "message": "Identity name" + "message": "Tapatybės vardas" }, "company": { - "message": "Company" + "message": "Įmonė" }, "ssn": { - "message": "Social Security number" + "message": "ID kortelės numeris" }, "passportNumber": { - "message": "Passport number" + "message": "Paso numeris" }, "licenseNumber": { - "message": "License number" + "message": "Licencijos numeris" }, "email": { - "message": "Email" + "message": "El. paštas" }, "phone": { - "message": "Phone" + "message": "Telefonas" }, "address": { - "message": "Address" + "message": "Adresas" }, "premiumRequired": { - "message": "Premium required" + "message": "Reikalinga Premium narystė" }, "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." + "message": "Premium narystė reikalinga šiai funkcijai naudoti." }, "errorOccurred": { - "message": "An error has occurred." + "message": "Įvyko klaida." }, "error": { - "message": "Error" + "message": "Klaida" }, "january": { - "message": "January" + "message": "Sausis" }, "february": { - "message": "February" + "message": "Vasaris" }, "march": { - "message": "March" + "message": "Kovas" }, "april": { - "message": "April" + "message": "Balandis" }, "may": { - "message": "May" + "message": "Gegužė" }, "june": { - "message": "June" + "message": "Birželis" }, "july": { - "message": "July" + "message": "Liepa" }, "august": { - "message": "August" + "message": "Rugpjūtis" }, "september": { - "message": "September" + "message": "Rugsėjis" }, "october": { - "message": "October" + "message": "Spalis" }, "november": { - "message": "November" + "message": "Lapkritis" }, "december": { - "message": "December" + "message": "Gruodis" }, "ex": { - "message": "ex.", + "message": "pvz.", "description": "Short abbreviation for 'example'." }, "title": { - "message": "Title" + "message": "Pavadinimas" }, "mr": { - "message": "Mr" + "message": "Ponas" }, "mrs": { - "message": "Mrs" + "message": "Ponia" }, "ms": { - "message": "Ms" + "message": "Panelė" }, "mx": { - "message": "Mx" + "message": "Neutralus (-i)" }, "dr": { "message": "Dr" }, "expirationMonth": { - "message": "Expiration month" + "message": "Galiojimo pabaigos mėnesis" }, "expirationYear": { - "message": "Expiration year" + "message": "Galiojimo pabaigos metai" }, "select": { - "message": "Select" + "message": "Pasirinkti" }, "other": { - "message": "Other" + "message": "Kita" }, "generatePassword": { - "message": "Generate password" + "message": "Sugeneruoti slaptažodį" }, "type": { - "message": "Type" + "message": "Tipas" }, "firstName": { - "message": "First name" + "message": "Vardas" }, "middleName": { - "message": "Middle name" + "message": "Antras vardas" }, "lastName": { - "message": "Last name" + "message": "Pavardė" }, "fullName": { - "message": "Full name" + "message": "Pilnas vardas" }, "address1": { - "message": "Address 1" + "message": "Adresas 1" }, "address2": { - "message": "Address 2" + "message": "Adresas 2" }, "address3": { - "message": "Address 3" + "message": "Adresas 3" }, "cityTown": { - "message": "City / Town" + "message": "Miestas" }, "stateProvince": { - "message": "State / Province" + "message": "Rajonas/apskritis" }, "zipPostalCode": { - "message": "Zip / Postal code" + "message": "Pašto kodas" }, "country": { - "message": "Country" + "message": "Šalis" }, "save": { - "message": "Save" + "message": "Išsaugoti" }, "cancel": { - "message": "Cancel" + "message": "Atšaukti" }, "delete": { - "message": "Delete" + "message": "Ištrinti" }, "favorite": { - "message": "Favorite" + "message": "Mėgstamiausias" }, "edit": { - "message": "Edit" + "message": "Redaguoti" }, "authenticatorKeyTotp": { - "message": "Authenticator key (TOTP)" + "message": "Autentifikavimo raktas (TOTP)" }, "folder": { - "message": "Folder" + "message": "Aplankas" }, "newCustomField": { - "message": "New custom field" + "message": "Naujas pasirinktinis laukelis" }, "value": { - "message": "Value" + "message": "Reikšmė" }, "dragToSort": { - "message": "Drag to sort" + "message": "Tempti, kad surūšiuoti" }, "cfTypeText": { - "message": "Text" + "message": "Tekstas" }, "cfTypeHidden": { - "message": "Hidden" + "message": "Paslėpta" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "Taip/Ne" }, "cfTypeLinked": { - "message": "Linked", + "message": "Susieta", "description": "This describes a field that is 'linked' (related) to another field." }, "linkedValue": { - "message": "Linked value", + "message": "Susieta reikšmė", "description": "This describes a value that is 'linked' (related) to another value." }, "remove": { - "message": "Remove" + "message": "Pašalinti" }, "nameRequired": { - "message": "Name is required." + "message": "Pavadinimas yra būtinas." }, "addedItem": { - "message": "Item added" + "message": "Elementas pridėtas" }, "editedItem": { - "message": "Item saved" + "message": "Elementas išsaugotas" }, "deleteItem": { - "message": "Delete item" + "message": "Šalinti elementą" }, "deleteFolder": { - "message": "Delete folder" + "message": "Šalinti aplanką" }, "deleteAttachment": { - "message": "Delete attachment" + "message": "Šalinti priedą" }, "deleteItemConfirmation": { - "message": "Do you really want to send to the trash?" + "message": "Ar tikrai norite perkelti į šiukšlinę?" }, "deletedItem": { - "message": "Item sent to trash" + "message": "Elementas perkeltas į šiukšlinę" }, "overwritePasswordConfirmation": { - "message": "Are you sure you want to overwrite the current password?" + "message": "Ar tikrai norite perrašyti dabartinį slaptažodį?" }, "overwriteUsername": { - "message": "Overwrite username" + "message": "Perrašyti vartotojo vardą" }, "overwriteUsernameConfirmation": { - "message": "Are you sure you want to overwrite the current username?" + "message": "Ar tikrai norite perrašyti dabartinį vartotojo vardą?" }, "noneFolder": { - "message": "No folder", + "message": "Nėra aplankų", "description": "This is the folder for uncategorized items" }, "addFolder": { - "message": "Add folder" + "message": "Pridėti aplanką" }, "editFolder": { - "message": "Edit folder" + "message": "Redaguoti aplanką" }, "regeneratePassword": { - "message": "Regenerate password" + "message": "Generuoti slaptažodį iš naujo" }, "copyPassword": { - "message": "Copy password" + "message": "Kopijuoti slaptažodį" }, "copyUri": { - "message": "Copy URI" + "message": "Kopijuoti nuorodą" }, "copyVerificationCodeTotp": { - "message": "Copy verification code (TOTP)" + "message": "Kopijuoti patvirtinimo kodą (TOTP)" }, "length": { - "message": "Length" + "message": "Ilgis" }, "uppercase": { - "message": "Uppercase (A-Z)" + "message": "Didžiosiomis (A-Z)" }, "lowercase": { - "message": "Lowercase (a-z)" + "message": "Mažosiomis (a-z)" }, "numbers": { - "message": "Numbers (0-9)" + "message": "Skaitmenys (0-9)" }, "specialCharacters": { - "message": "Special characters (!@#$%^&*)" + "message": "Specialieji simboliai (!@#$%^&*)" }, "numWords": { - "message": "Number of words" + "message": "Žodžių skaičius" }, "wordSeparator": { - "message": "Word separator" + "message": "Žodžių skirtukas" }, "capitalize": { - "message": "Capitalize", + "message": "Pradėti didžiosiomis", "description": "Make the first letter of a word uppercase." }, "includeNumber": { - "message": "Include number" + "message": "Įtraukti skaičius" }, "close": { - "message": "Close" + "message": "Uždaryti" }, "minNumbers": { - "message": "Minimum numbers" + "message": "Mažiausias skaičių kiekis" }, "minSpecial": { - "message": "Minimum special", + "message": "Mažiausias spec. simbolių kiekis", "description": "Minimum Special Characters" }, "ambiguous": { - "message": "Avoid ambiguous characters" + "message": "Vengti dviprasmiškų simbolių" }, "searchCollection": { - "message": "Search collection" + "message": "Ieškoti rinkinyje" }, "searchFolder": { - "message": "Search folder" + "message": "Ieškoti aplanke" }, "searchFavorites": { - "message": "Search favorites" + "message": "Ieškoti mėgstamiausiuose" }, "searchType": { - "message": "Search type", + "message": "Paieškos tipas", "description": "Search item type" }, "newAttachment": { - "message": "Add new attachment" + "message": "Pridėti naują priedą" }, "deletedAttachment": { - "message": "Attachment deleted" + "message": "Priedas ištrintas" }, "deleteAttachmentConfirmation": { - "message": "Are you sure you want to delete this attachment?" + "message": "Ar esate tikri, kad norite ištrinti šį priedą?" }, "attachmentSaved": { - "message": "Attachment saved" + "message": "Priedas išsaugotas" }, "file": { - "message": "File" + "message": "Failas" }, "selectFile": { - "message": "Select a file" + "message": "Pasirinkite failą" }, "maxFileSize": { - "message": "Maximum file size is 500 MB." + "message": "Didžiausias failo dydis – 500 MB." }, "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "message": "Negalite naudoti šios funkcijos, kol neatnaujinsite šifravimo rakto." }, "editedFolder": { - "message": "Folder saved" + "message": "Aplankas išsaugotas" }, "addedFolder": { - "message": "Folder added" + "message": "Aplankas pridėtas" }, "deleteFolderConfirmation": { - "message": "Are you sure you want to delete this folder?" + "message": "Ar tikrai norite ištrinti šį aplanką?" }, "deletedFolder": { - "message": "Folder deleted" + "message": "Aplankas ištrintas" }, "loginOrCreateNewAccount": { - "message": "Log in or create a new account to access your secure vault." + "message": "Prisijunkite arba sukurkite naują paskyrą, kad galėtumėte pasiekti saugyklą." }, "createAccount": { - "message": "Create account" + "message": "Sukurti paskyrą" }, "logIn": { - "message": "Log in" + "message": "Prisijungti" }, "submit": { - "message": "Submit" + "message": "Išsaugoti" }, "masterPass": { - "message": "Master password" + "message": "Pagrindinis slaptažodis" }, "masterPassDesc": { - "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + "message": "Pagrindinis slaptažodis yra slaptažodis, kurį naudojate norėdami pasiekti savo saugyklą. Labai svarbu nepamiršti pagrindinio slaptažodžio. Negalėsite atkurti slaptažodį, jei jį pamiršote." }, "masterPassHintDesc": { - "message": "A master password hint can help you remember your password if you forget it." + "message": "Pagrindinio slaptažodžio užuomina gali padėti prisiminti slaptažodį, jei jį pamiršite." }, "reTypeMasterPass": { - "message": "Re-type master password" + "message": "Pakartokite pagrindinį slaptažodį" }, "masterPassHint": { - "message": "Master password hint (optional)" + "message": "Pagrindinio slaptažodžio užuomina (neprivaloma)" }, "settings": { - "message": "Settings" + "message": "Nustatymai" }, "passwordHint": { - "message": "Password hint" + "message": "Slaptažodžio užuomina" }, "enterEmailToGetHint": { - "message": "Enter your account email address to receive your master password hint." + "message": "Įveskite savo paskyros el. pašto adresą, kad gautumėte pagrindinio slaptažodžio užuominą." }, "getMasterPasswordHint": { - "message": "Get master password hint" + "message": "Gauti pagrindinio slaptažodžio užuominą" }, "emailRequired": { - "message": "Email address is required." + "message": "Reikalingas el. pašto adresas." }, "invalidEmail": { - "message": "Invalid email address." + "message": "Netinkamas el. pašto adresas." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Būtinas pagrindinis slaptažodis." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Būtinas prisijungimo slaptažodžio patvirtinimas." }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "Pagrindinis slaptažodis turi būti bent $VALUE$ simbolių ilgio.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -552,52 +552,52 @@ } }, "masterPassDoesntMatch": { - "message": "Master password confirmation does not match." + "message": "Pagrindinio slaptažodžio patvirtinimas nesutampa." }, "newAccountCreated": { - "message": "Your new account has been created! You may now log in." + "message": "Jūsų paskyra sukurta! Galite prisijungti." }, "masterPassSent": { - "message": "We've sent you an email with your master password hint." + "message": "Išsiuntėme jums el. laišką su pagrindinio slaptažodžio užuomina." }, "unexpectedError": { - "message": "An unexpected error has occurred." + "message": "Įvyko netikėta klaida." }, "itemInformation": { - "message": "Item information" + "message": "Elemento informacija" }, "noItemsInList": { - "message": "There are no items to list." + "message": "Nėra rodytinų elementų." }, "sendVerificationCode": { - "message": "Send a verification code to your email" + "message": "Siųsti patvirtinimo kodą į el. paštą" }, "sendCode": { - "message": "Send code" + "message": "Siųsti kodą" }, "codeSent": { - "message": "Code sent" + "message": "Kodas išsiųstas" }, "verificationCode": { - "message": "Verification code" + "message": "Patvirtinimo kodas" }, "confirmIdentity": { - "message": "Confirm your identity to continue." + "message": "Norint tęsti, patvirtinkite tapatybę." }, "verificationCodeRequired": { - "message": "Verification code is required." + "message": "Būtinas patvirtinimo kodas." }, "invalidVerificationCode": { - "message": "Invalid verification code" + "message": "Neteisingas patvirtinimo kodas" }, "continue": { - "message": "Continue" + "message": "Tęsti" }, "enterVerificationCodeApp": { - "message": "Enter the 6 digit verification code from your authenticator app." + "message": "Įveskite 6 skaitmenų patvirtinimo kodą iš autentifikavimo programos." }, "enterVerificationCodeEmail": { - "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "message": "Įveskite 6 skaitmenų patvirtinimo kodą, kuris buvo išsiųstas $EMAIL$ el. paštu.", "placeholders": { "email": { "content": "$1", @@ -606,7 +606,7 @@ } }, "verificationCodeEmailSent": { - "message": "Verification email sent to $EMAIL$.", + "message": "Patvirtinimo laiškas išsiųstas į $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -615,216 +615,216 @@ } }, "rememberMe": { - "message": "Remember me" + "message": "Prisiminti mane" }, "sendVerificationCodeEmailAgain": { - "message": "Send verification code email again" + "message": "Pakartotinai atsiųsti patvirtinimo kodą el. paštu" }, "useAnotherTwoStepMethod": { - "message": "Use another two-step login method" + "message": "Naudoti kitą dviejų žingsnių prisijungimo metodą" }, "insertYubiKey": { - "message": "Insert your YubiKey into your computer's USB port, then touch its button." + "message": "Įkiškite YubiKey į savo kompiuterio USB prievadą, tada palieskite jo mygtuką." }, "insertU2f": { - "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + "message": "Įkiškite savo saugos raktą į kompiuterio USB prievadą. Jei jame yra mygtukas, palieskite jį." }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers on your account." + "message": "Praradote prieigą prie visų savo dviejų faktorių tiekėjų? Naudokite atkūrimo kodą, kad iš savo paskyros išjungtumėte visus dviejų faktorių tiekėjus." }, "recoveryCodeTitle": { - "message": "Recovery code" + "message": "Atkūrimo kodas" }, "authenticatorAppTitle": { - "message": "Authenticator app" + "message": "Autentifikavimo programa" }, "authenticatorAppDesc": { - "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "message": "Naudokite autentifikatoriaus programėlę (pvz. Authy arba Google Authenticator), kad sugeneruotumėte laiko patikrinimo kodus.", "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { - "message": "YubiKey OTP security key" + "message": "YubiKey OTP saugumo raktas" }, "yubiKeyDesc": { - "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + "message": "Naudokite YubiKey, kad prisijungtumėte prie savo paskyros. Veikia su YubiKey 4, 4 Nano, 4C ir NEO įrenginiais." }, "duoDesc": { - "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "message": "Patvirtinkite su Duo Security naudodami Duo Mobile programą, SMS žinutę, telefono skambutį arba U2F saugumo raktą.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", + "message": "Patikrinkite su Duo Security savo organizacijai naudodami Duo Mobile programą, SMS žinutę, telefono skambutį arba U2F saugumo raktą.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Use any WebAuthn compatible security key to access your account." + "message": "Naudokite bet kurį WebAuthn palaikantį saugumo raktą, kad galėtumėte naudotis savo paskyra." }, "emailTitle": { - "message": "Email" + "message": "El. paštas" }, "emailDesc": { - "message": "Verification codes will be emailed to you." + "message": "Patvirtinimo kodai bus atsiųsti el. paštu." }, "loginUnavailable": { - "message": "Login unavailable" + "message": "Prisijungimas nepasiekiamas" }, "noTwoStepProviders": { - "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this device." + "message": "Šioje paskyroje nustatytas dviejų žingsnių prisijungimas, tačiau nė vienas iš sukonfigūruotų dviejų žingsnių paslaugų teikėjų nėra palaikomi šiame įrenginyje." }, "noTwoStepProviders2": { - "message": "Please add additional providers that are better supported across devices (such as an authenticator app)." + "message": "Prašome pridėti papildomus tiekėjus, kurie labiau palaikomi tarp įrenginių (pvz. autentifikavimo programėlę)." }, "twoStepOptions": { - "message": "Two-step login options" + "message": "Dviejų žingsnių prisijungimo parinktys" }, "selfHostedEnvironment": { - "message": "Self-hosted environment" + "message": "Savarankiškai sukurta aplinka" }, "selfHostedEnvironmentFooter": { - "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + "message": "Nurodykite pagrindinį URL adresą savo patalpose esančio Bitwarden įdiegimo." }, "customEnvironment": { - "message": "Custom environment" + "message": "Individualizuota aplinka" }, "customEnvironmentFooter": { - "message": "For advanced users. You can specify the base URL of each service independently." + "message": "Pažengusiems naudotojams. Galite nurodyti kiekvienos paslaugos pagrindinį URL adresą atskirai." }, "baseUrl": { - "message": "Server URL" + "message": "Serverio nuoroda" }, "apiUrl": { - "message": "API server URL" + "message": "API serverio nuoroda" }, "webVaultUrl": { - "message": "Web vault server URL" + "message": "Internetinės saugyklos serverio nuoroda" }, "identityUrl": { - "message": "Identity server URL" + "message": "Identifikavimo serverio nuoroda" }, "notificationsUrl": { - "message": "Notifications server URL" + "message": "Notifikacijų serverio nuoroda" }, "iconsUrl": { - "message": "Icons server URL" + "message": "Piktogramų serverio nuoroda" }, "environmentSaved": { - "message": "Environment URLs saved" + "message": "Aplinkos URL nuorodos išsaugotos" }, "ok": { - "message": "Ok" + "message": "Gerai" }, "yes": { - "message": "Yes" + "message": "Taip" }, "no": { - "message": "No" + "message": "Ne" }, "overwritePassword": { - "message": "Overwrite password" + "message": "Perrašyti slaptažodį" }, "learnMore": { - "message": "Learn more" + "message": "Sužinoti daugiau" }, "featureUnavailable": { - "message": "Feature unavailable" + "message": "Funkcija nepasiekiama" }, "loggedOut": { - "message": "Logged out" + "message": "Atsijungta" }, "loginExpired": { - "message": "Your login session has expired." + "message": "Sesijos laikas baigėsi." }, "logOutConfirmation": { - "message": "Are you sure you want to log out?" + "message": "Ar tikrai norite atsijungti?" }, "logOut": { - "message": "Log out" + "message": "Atsijungti" }, "addNewLogin": { - "message": "New login" + "message": "Naujas prisijungimas" }, "addNewItem": { - "message": "New item" + "message": "Naujas elementas" }, "addNewFolder": { - "message": "New folder" + "message": "Naujas aplankas" }, "view": { - "message": "View" + "message": "Peržiūrėti" }, "account": { - "message": "Account" + "message": "Paskyra" }, "loading": { - "message": "Loading..." + "message": "Įkeliama..." }, "lockVault": { - "message": "Lock vault" + "message": "Užrakinti saugyklą" }, "passwordGenerator": { - "message": "Password generator" + "message": "Slaptažodžių generatorius" }, "contactUs": { - "message": "Contact us" + "message": "Susisiekite" }, "helpAndFeedback": { - "message": "Help and feedback" + "message": "Pagalba ir atsiliepimai" }, "getHelp": { - "message": "Get help" + "message": "Gauti pagalbą" }, "fileBugReport": { - "message": "File a bug report" + "message": "Pateikite pranešimą apie klaidą" }, "blog": { - "message": "Blog" + "message": "Tinklaraštis" }, "followUs": { - "message": "Follow us" + "message": "Sekite mus" }, "syncVault": { - "message": "Sync vault" + "message": "Sinchronizuoti saugyklą" }, "changeMasterPass": { - "message": "Change master password" + "message": "Keisti pagrindinį slaptažodį" }, "changeMasterPasswordConfirmation": { - "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Pagrindinį slaptažodį galite pakeisti bitwarden.com žiniatinklio saugykloje. Ar norite dabar apsilankyti svetainėje?" }, "fingerprintPhrase": { - "message": "Fingerprint phrase", + "message": "Piršto antspaudo frazė", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "yourAccountsFingerprint": { - "message": "Your account's fingerprint phrase", + "message": "Jūsų paskyros piršto antspaudo frazė", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "goToWebVault": { - "message": "Go to web vault" + "message": "Eiti į mano saugyklą" }, "getMobileApp": { - "message": "Get mobile app" + "message": "Gauti mobiliąją aplikaciją" }, "getBrowserExtension": { - "message": "Get browser extension" + "message": "Gauti naršyklės plėtinį" }, "syncingComplete": { - "message": "Syncing complete" + "message": "Sinchronizacija baigta" }, "syncingFailed": { - "message": "Syncing failed" + "message": "Sinchronizuoti nepavyko" }, "yourVaultIsLocked": { - "message": "Your vault is locked. Verify your identity to continue." + "message": "Jūsų saugykla užrakinta. Norėdami tęsti, patvirtinkite savo tapatybę." }, "unlock": { - "message": "Unlock" + "message": "Atrakinti" }, "loggedInAsOn": { - "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "message": "Prisijungta prie $HOSTNAME$ kaip $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -837,16 +837,16 @@ } }, "invalidMasterPassword": { - "message": "Invalid master password" + "message": "Neteisingas pagrindinis slaptažodis" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Prisijungus dviem veiksmais, jūsų paskyra tampa saugesnė, reikalaujant patvirtinti prisijungimą naudojant kitą įrenginį, pvz., Saugos raktą, autentifikavimo programą, SMS, telefono skambutį ar el. Paštą. Dviejų žingsnių prisijungimą galima įjungti „bitwarden.com“ interneto saugykloje. Ar norite dabar apsilankyti svetainėje?" }, "twoStepLogin": { - "message": "Two-step login" + "message": "Dviejų žingsnių prisijungimas" }, "vaultTimeout": { - "message": "Vault timeout" + "message": "Atsijungta nuo saugyklos" }, "vaultTimeoutDesc": { "message": "Choose when your vault will take the vault timeout action." @@ -962,49 +962,49 @@ "message": "Start automatically on login" }, "openAtLoginDesc": { - "message": "Start the Bitwarden desktop application automatically on login." + "message": "Prisijungus pradėti Bitwarden darbalaukio aplikaciją automatiškai." }, "alwaysShowDock": { - "message": "Always show in the Dock" + "message": "Visada rodyti Dock" }, "alwaysShowDockDesc": { - "message": "Show the Bitwarden icon in the Dock even when minimized to the menu bar." + "message": "Rodyti Bitwarden ikoną, esančią Dock, net kai ji sumažinta į meniu juostą." }, "confirmTrayTitle": { - "message": "Confirm hiding tray" + "message": "Patvirtinti sumažinimą informacijos srityje" }, "confirmTrayDesc": { - "message": "Turning off this setting will also turn off all other tray related settings." + "message": "Išjungus šį nustatymą bus išjungti visi kiti su informacijos sritimi susiję nustatymai." }, "language": { - "message": "Language" + "message": "Kalba" }, "languageDesc": { - "message": "Change the language used by the application. Restart is required." + "message": "Pakeisti aplikacijos naudojamą kalbą. Privaloma paleisti iš naujo." }, "theme": { - "message": "Theme" + "message": "Tema" }, "themeDesc": { - "message": "Change the application's color theme." + "message": "Pakeisti programos spalvos temą." }, "dark": { - "message": "Dark", + "message": "Tamsi", "description": "Dark color" }, "light": { - "message": "Light", + "message": "Šviesi", "description": "Light color" }, "copy": { - "message": "Copy", + "message": "Kopijuoti", "description": "Copy to clipboard" }, "checkForUpdates": { - "message": "Check for updates…" + "message": "Tikrinti, ar yra atnaujinimų…" }, "version": { - "message": "Version $VERSION_NUM$", + "message": "Versija $VERSION_NUM$", "placeholders": { "version_num": { "content": "$1", @@ -1013,10 +1013,10 @@ } }, "restartToUpdate": { - "message": "Restart to update" + "message": "Norint atnaujinti, perkraukite" }, "restartToUpdateDesc": { - "message": "Version $VERSION_NUM$ is ready to install. You must restart the application to complete the installation. Do you want to restart and update now?", + "message": "Versija $VERSION_NUM$ yra paruošta diegimui. Privalote perkrauti aplikaciją norint užbaigti diegimą. Ar norite perkrauti ir atsinaujinti dabar?", "placeholders": { "version_num": { "content": "$1", @@ -1025,87 +1025,87 @@ } }, "updateAvailable": { - "message": "Update available" + "message": "Prieinamas atnaujinimas" }, "updateAvailableDesc": { - "message": "An update was found. Do you want to download it now?" + "message": "Rastas atnaujinimas. Ar norite jį parsisiųsti dabar?" }, "restart": { - "message": "Restart" + "message": "Paleisti iš naujo" }, "later": { - "message": "Later" + "message": "Vėliau" }, "noUpdatesAvailable": { - "message": "No updates are currently available. You are using the latest version." + "message": "Nerasta jokių atnaujinimų. Naudojate naujausią versiją." }, "updateError": { - "message": "Update error" + "message": "Atnaujinimo klaida" }, "unknown": { - "message": "Unknown" + "message": "Nežinoma" }, "copyUsername": { - "message": "Copy username" + "message": "Kopijuoti vartotojo vardą" }, "copyNumber": { - "message": "Copy number", + "message": "Kopijuoti numerį", "description": "Copy credit card number" }, "copySecurityCode": { - "message": "Copy security code", + "message": "Kopijuoti saugos kodą", "description": "Copy credit card security code (CVV)" }, "premiumMembership": { - "message": "Premium membership" + "message": "Premium narystė" }, "premiumManage": { - "message": "Manage membership" + "message": "Tvarkyti narystę" }, "premiumManageAlert": { - "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Gali tvarkyti savo Premium narystę bitwarden.com interneto saugykloje. Ar norite aplankyti svetainę dabar?" }, "premiumRefresh": { - "message": "Refresh membership" + "message": "Atnaujinti narystę" }, "premiumNotCurrentMember": { - "message": "You are not currently a Premium member." + "message": "Neturite Premium narystės." }, "premiumSignUpAndGet": { - "message": "Sign up for a Premium membership and get:" + "message": "Prisijunkite prie Premium narystės ir gaukite:" }, "premiumSignUpStorage": { - "message": "1 GB encrypted storage for file attachments." + "message": "1 GB užšifruotos vietos diske failų prisegimams." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Patentuotos dviejų žingsnių prisijungimo parinktys, tokios kaip YubiKey ir Duo." }, "premiumSignUpReports": { - "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + "message": "Slaptažodžio higiena, paskyros sveikata ir duomenų nutekinimo ataskaitos, kad jūsų saugykla būtų saugi." }, "premiumSignUpTotp": { - "message": "TOTP verification code (2FA) generator for logins in your vault." + "message": "TOTP patvirtinimo kodų (2FA) generatorius prisijungimams prie jūsų saugyklos." }, "premiumSignUpSupport": { - "message": "Priority customer support." + "message": "Prioritetinis klientų aptarnavimas." }, "premiumSignUpFuture": { - "message": "All future premium features. More coming soon!" + "message": "Visos būsimos Premium savybės. Daugiau jau greitai!" }, "premiumPurchase": { - "message": "Purchase Premium" + "message": "Įsigyti Premium" }, "premiumPurchaseAlert": { - "message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Galite įsigyti Premium narystę bitwarden.com interneto saugykloje. Ar norite aplankyti svetainę dabar?" }, "premiumCurrentMember": { - "message": "You are a premium member!" + "message": "Esate Premium narys!" }, "premiumCurrentMemberThanks": { - "message": "Thank you for supporting Bitwarden." + "message": "Dėkojame, kad remiate Bitwarden." }, "premiumPrice": { - "message": "All for just $PRICE$ /year!", + "message": "Visa tai tik už $PRICE$ / metus!", "placeholders": { "price": { "content": "$1", @@ -1114,84 +1114,84 @@ } }, "refreshComplete": { - "message": "Refresh complete" + "message": "Atnaujinimas įvykdytas" }, "passwordHistory": { - "message": "Password history" + "message": "Slaptažodžių istorija" }, "clear": { - "message": "Clear", + "message": "Išvalyti", "description": "To clear something out. example: To clear browser history." }, "noPasswordsInList": { - "message": "There are no passwords to list." + "message": "Nėra rodytinų slaptažodžių." }, "undo": { - "message": "Undo" + "message": "Anuliuoti" }, "redo": { - "message": "Redo" + "message": "Grąžinti" }, "cut": { - "message": "Cut", + "message": "Iškirpti", "description": "Cut to clipboard" }, "paste": { - "message": "Paste", + "message": "Įklijuoti", "description": "Paste from clipboard" }, "selectAll": { - "message": "Select all" + "message": "Pažymėti visus" }, "zoomIn": { - "message": "Zoom in" + "message": "Priartinti" }, "zoomOut": { - "message": "Zoom out" + "message": "Nutolinti" }, "resetZoom": { - "message": "Reset zoom" + "message": "Atstatyti mastelį" }, "toggleFullScreen": { - "message": "Toggle full screen" + "message": "Perjungti viso ekrano režimą" }, "reload": { - "message": "Reload" + "message": "Perkrauti" }, "toggleDevTools": { - "message": "Toggle developer tools" + "message": "Įjungti kūrėjo įrankius" }, "minimize": { - "message": "Minimize", + "message": "Sumažinti", "description": "Minimize window" }, "zoom": { - "message": "Zoom" + "message": "Mastelis" }, "bringAllToFront": { - "message": "Bring all to front", + "message": "Perkelti visus į priekį", "description": "Bring all windows to front (foreground)" }, "aboutBitwarden": { - "message": "About Bitwarden" + "message": "Apie Bitwarden" }, "services": { - "message": "Services" + "message": "Paslaugos" }, "hideBitwarden": { - "message": "Hide Bitwarden" + "message": "Paslėpti Bitwarden" }, "hideOthers": { - "message": "Hide others" + "message": "Slėpti kitus" }, "showAll": { - "message": "Show all" + "message": "Rodyti viską" }, "quitBitwarden": { - "message": "Quit Bitwarden" + "message": "Išeiti iš Bitwarden" }, "valueCopied": { - "message": "$VALUE$ copied", + "message": "$VALUE$ nukopijuota", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -1201,16 +1201,16 @@ } }, "help": { - "message": "Help" + "message": "Pagalba" }, "window": { - "message": "Window" + "message": "Langas" }, "checkPassword": { - "message": "Check if password has been exposed." + "message": "Patikrinti ar slaptažodis buvo atskleistas." }, "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "message": "Šis slaptažodis buvo atskleistas $VALUE$ kartą (-us) dėl duomenų pažeidimų. Turėtumėte jį pakeisti.", "placeholders": { "value": { "content": "$1", @@ -1219,156 +1219,156 @@ } }, "passwordSafe": { - "message": "This password was not found in any known data breaches. It should be safe to use." + "message": "Šis slaptažodis nebuvo rastas per jokius žinomus duomenų pažeidimus. Jis turėtų būti saugus naudoti." }, "baseDomain": { - "message": "Base domain", + "message": "Bazinis domenas", "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Domeno pavadinimas", "description": "Domain name. Ex. website.com" }, "host": { - "message": "Host", + "message": "Serveris", "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { - "message": "Exact" + "message": "Tikslus" }, "startsWith": { - "message": "Starts with" + "message": "Prasideda su" }, "regEx": { - "message": "Regular expression", + "message": "Reguliari išraiška", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "Match detection", + "message": "Atitikmens aptikimas", "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { - "message": "Default match detection", + "message": "Numatytasis atitikties aptikimas", "description": "Default URI match detection for auto-fill." }, "toggleOptions": { - "message": "Toggle options" + "message": "Perjungti nustatymus" }, "organization": { - "message": "Organization", + "message": "Organizacija", "description": "An entity of multiple related people (ex. a team or business organization)." }, "default": { - "message": "Default" + "message": "Numatytas" }, "exit": { - "message": "Exit" + "message": "Išeiti" }, "showHide": { - "message": "Show / Hide", + "message": "Rodyti / Slėpti", "description": "Text for a button that toggles the visibility of the window. Shows the window when it is hidden or hides the window if it is currently open." }, "hideToTray": { - "message": "Hide to tray" + "message": "Paslėpti" }, "alwaysOnTop": { - "message": "Always on top", + "message": "Visada viršuje", "description": "Application window should always stay on top of other windows" }, "dateUpdated": { - "message": "Updated", + "message": "Atnaujintas", "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Sukurtas", "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "Password updated", + "message": "Slaptažodis atnaujintas", "description": "ex. Date this password was updated" }, "exportVault": { - "message": "Export vault" + "message": "Eksportuoti saugyklą" }, "fileFormat": { - "message": "File format" + "message": "Failo formatas" }, "hCaptchaUrl": { - "message": "hCaptcha Url", + "message": "hCaptcha nuoroda", "description": "hCaptcha is the name of a website, should not be translated" }, "loadAccessibilityCookie": { - "message": "Load accessibility cookie" + "message": "Užkrauti prieinamumo slapuką" }, "registerAccessibilityUser": { - "message": "Register as an accessibility user at", + "message": "Registruotis kaip prieinamumo vartotojas", "description": "ex. Register as an accessibility user at hcaptcha.com" }, "copyPasteLink": { - "message": "Copy and paste the link sent to your email below" + "message": "Nukopijuokite ir įklijuokite adresą atsiųstą į jūsų paštą apačioje" }, "enterhCaptchaUrl": { - "message": "Enter URL to load accessibility cookie for hCaptcha", + "message": "Įveskite nuorodą reikalingą hCaptcha prieinamumo slapukui", "description": "hCaptcha is the name of a website, should not be translated" }, "hCaptchaUrlRequired": { - "message": "hCaptcha Url is required", + "message": "hCaptcha nuoroda yra privaloma", "description": "hCaptcha is the name of a website, should not be translated" }, "invalidUrl": { - "message": "Invalid Url" + "message": "Klaidingas URL" }, "done": { - "message": "Done" + "message": "Atlikta" }, "accessibilityCookieSaved": { - "message": "Accessibility cookie saved!" + "message": "Prieinamumo slapukai išsaugoti!" }, "noAccessibilityCookieSaved": { - "message": "No accessibility cookie saved" + "message": "Prieinamumo slapukas neišsaugotas" }, "warning": { - "message": "WARNING", + "message": "ĮSPĖJIMAS", "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Confirm vault export" + "message": "Patvirtinkite saugyklos eksportavimą" }, "exportWarningDesc": { - "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it." + "message": "Šiame duomenų eksporte jūsų saugyklos duomenys yra neužšifruoti. Jūs neturėtumete laikyti ar siųsti išeksportuotos duomenų bylos nesaugiu komunikaciniu kanalu (tokiu kaip el. paštas). Ištrinkite jį kaip galima greičiau po to kai pasinaudojote." }, "encExportKeyWarningDesc": { - "message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file." + "message": "Šis duomenų eksportavimas užšifruoja jūsų duomenis naudodamas jūsų paskyros šifravimo raktą. Jei jūs kada nuspręsite pakeisti paskyros šifravimo raktą, turėtumėte iš naujo eksportuoti duomenis, nes kitaip jūs negalėsite atšifruoti išeksportuotų duomenų." }, "encExportAccountWarningDesc": { - "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + "message": "Paskyros šifravimo raktai yra unikalūs kiekvienai Bitwarden vartotojo paskyrai, taigi jums nepavyktų importuoti užkoduotų eksportuotų duomenų į kitą paskyrą." }, "noOrganizationsList": { - "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + "message": "Jūs nepriklausote jokiai organizacijai. Organizacijos leidžia saugiai dalintis elementais su kitais vartotojais." }, "noCollectionsInList": { - "message": "There are no collections to list." + "message": "Nėra rodytinų rinkinių." }, "ownership": { - "message": "Ownership" + "message": "Nuosavybė" }, "whoOwnsThisItem": { - "message": "Who owns this item?" + "message": "Kam priklauso šis elementas?" }, "strong": { - "message": "Strong", + "message": "Stiprus", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "Good", + "message": "Geras", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { - "message": "Weak", + "message": "Silpnas", "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Weak master password" + "message": "Silpnas pagrindinis slaptažodis" }, "weakMasterPasswordDesc": { "message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?" @@ -1411,145 +1411,145 @@ "message": "Ask for Windows Hello on app start" }, "autoPromptTouchId": { - "message": "Ask for Touch ID on app start" + "message": "Prašyti Touch ID paleidus programėlę" }, "requirePasswordOnStart": { - "message": "Require password or PIN on app start" + "message": "Reikalauti slaptažodžio arba PIN paleidus programėlę" }, "recommendedForSecurity": { - "message": "Recommended for security." + "message": "Rekomenduojama dėl saugumo." }, "lockWithMasterPassOnRestart": { - "message": "Lock with master password on restart" + "message": "Užrakinti su pagrindiniu slaptažodžiu perkrovus" }, "deleteAccount": { - "message": "Delete account" + "message": "Ištrinti paskyrą" }, "deleteAccountDesc": { - "message": "Proceed below to delete your account and all vault data." + "message": "Tęskite apačioje norėdami ištrinti savo paskyrą ir visus saugyklos duomenis." }, "deleteAccountWarning": { - "message": "Deleting your account is permanent. It cannot be undone." + "message": "Paskyros ištrinimas yra amžinas. Jos nebus galima atkurti." }, "accountDeleted": { - "message": "Account deleted" + "message": "Paskyra ištrinta" }, "accountDeletedDesc": { - "message": "Your account has been closed and all associated data has been deleted." + "message": "Jūsų paskyra buvo uždaryta ir visi su ja susiję duomenys buvo ištrinti." }, "preferences": { - "message": "Preferences" + "message": "Nuostatos" }, "enableMenuBar": { - "message": "Show menu bar icon" + "message": "Rodyti meniu juostos ikoną" }, "enableMenuBarDesc": { - "message": "Always show an icon in the menu bar." + "message": "Visada rodyti ikoną meniu juostoje." }, "hideToMenuBar": { - "message": "Hide to menu bar" + "message": "Paslėpti meniu juostoje" }, "selectOneCollection": { - "message": "You must select at least one collection." + "message": "Turite pasirinkti bent vieną rinkinį." }, "premiumUpdated": { - "message": "You've upgraded to Premium." + "message": "Jūs įsigijote Premium." }, "restore": { - "message": "Restore" + "message": "Atkurti" }, "premiumManageAlertAppStore": { - "message": "You can manage your subscription from the App Store. Do you want to visit the App Store now?" + "message": "Galite apžvelgti savo prenumeratą App Store. Ar norite aplankyti App Store dabar?" }, "legal": { - "message": "Legal", + "message": "Teisinės", "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." }, "termsOfService": { - "message": "Terms of Service" + "message": "Paslaugų teikimo sąlygos" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "Privatumo politika" }, "unsavedChangesConfirmation": { - "message": "Are you sure you want to leave? If you leave now then your current information will not be saved." + "message": "Ar jūs tikrai norite išeiti? Jei išeisite dabar dabartinė informacija nebus išsaugota." }, "unsavedChangesTitle": { - "message": "Unsaved changes" + "message": "Neišsaugoti pakeitimai" }, "clone": { - "message": "Clone" + "message": "Klonuoti" }, "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." + "message": "Viena ar daugiau organizacijos politikų turi įtakos jūsų generatoriaus nustatymams." }, "vaultTimeoutAction": { - "message": "Vault timeout action" + "message": "Saugyklos skirtojo laiko veiksmas" }, "vaultTimeoutActionLockDesc": { - "message": "Master password or other unlock method is required to access your vault again." + "message": "Pagrindinis slaptažodis arba kitas atrakinimo būdas yra privalomas norint vėl prieiti prie jūsų saugyklos." }, "vaultTimeoutActionLogOutDesc": { - "message": "Re-authentication is required to access your vault again." + "message": "Privaloma autentifikuotis iš naujo norint vėl prieiti prie savo saugyklos." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Nustatykite atrakinimo būdą, kad pakeistumėte saugyklos laiko limito veiksmą." }, "lock": { - "message": "Lock", + "message": "Užrakinti", "description": "Verb form: to make secure or inaccesible by" }, "trash": { - "message": "Trash", + "message": "Šiukšliadėžė", "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { - "message": "Search trash" + "message": "Ieškoti šiukšliadėžėje" }, "permanentlyDeleteItem": { - "message": "Permanently delete item" + "message": "Ištrinti visam laikui" }, "permanentlyDeleteItemConfirmation": { - "message": "Are you sure you want to permanently delete this item?" + "message": "Ar tikrai norite visam laikui ištrinti šį elementą?" }, "permanentlyDeletedItem": { - "message": "Item permanently deleted" + "message": "Elementas ištrintas visam laikui" }, "restoredItem": { - "message": "Item restored" + "message": "Elementas atkurtas" }, "permanentlyDelete": { - "message": "Permanently delete" + "message": "Ištrinti visam laikui" }, "vaultTimeoutLogOutConfirmation": { "message": "Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Timeout action confirmation" + "message": "Laiko limito atjungimo veiksmo patvirtinimas" }, "enterpriseSingleSignOn": { - "message": "Enterprise single sign-on" + "message": "Vienkartinis įmonės prisijungimas" }, "setMasterPassword": { - "message": "Set master password" + "message": "Nustatyti pagrindinį slaptažodį" }, "ssoCompleteRegistration": { - "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." + "message": "Kad užbaigtumėte prisijungimą naudodami SSO, nustatykite pagrindinį slaptažodį, kad galėtumėte pasiekti ir apsaugoti savo saugyklą." }, "currentMasterPass": { - "message": "Current master password" + "message": "Dabartinis pagrindinis slaptažodis" }, "newMasterPass": { - "message": "New master password" + "message": "Naujas pagrindinis slaptažodis" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "Patvirtinti naują pagrindinį slaptažodį" }, "masterPasswordPolicyInEffect": { - "message": "One or more organization policies require your master password to meet the following requirements:" + "message": "Viena ar daugiau organizacijos politikos reikalauja, kad jūsų pagrindinis slaptažodis atitiktų šiuos reikalavimus:" }, "policyInEffectMinComplexity": { - "message": "Minimum complexity score of $SCORE$", + "message": "Minimalus sudėtingumo balas $SCORE$", "placeholders": { "score": { "content": "$1", @@ -1558,7 +1558,7 @@ } }, "policyInEffectMinLength": { - "message": "Minimum length of $LENGTH$", + "message": "Minimalus ilgis $LENGTH$", "placeholders": { "length": { "content": "$1", @@ -1567,16 +1567,16 @@ } }, "policyInEffectUppercase": { - "message": "Contain one or more uppercase characters" + "message": "Turi vieną ar daugiau didžiųjų raidžių" }, "policyInEffectLowercase": { - "message": "Contain one or more lowercase characters" + "message": "Turi vieną ar daugiau mažųjų raidžių" }, "policyInEffectNumbers": { - "message": "Contain one or more numbers" + "message": "Turi vieną ar daugiau skaičių" }, "policyInEffectSpecial": { - "message": "Contain one or more of the following special characters $CHARS$", + "message": "Turi vieną ar daugiau šių specialiųjų simbolių: $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -1585,55 +1585,55 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Your new master password does not meet the policy requirements." + "message": "Jūsų naujasis pagrindinis slaptažodis neatitinka politikos reikalavimų." }, "acceptPolicies": { - "message": "By checking this box you agree to the following:" + "message": "Pažymėdami šį laukelį, sutinkate su šiais dalykais:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Paslaugų teikimo sąlygos ir privatumo politika nebuvo pripažinti." }, "enableBrowserIntegration": { - "message": "Allow browser integration" + "message": "Leisti naršyklės integravimą" }, "enableBrowserIntegrationDesc": { - "message": "Used for biometrics in browser." + "message": "Naudojama biometrikai naršyklėje." }, "enableDuckDuckGoBrowserIntegration": { - "message": "Allow DuckDuckGo browser integration" + "message": "Leisti DuckDuckGo naršyklės integravimą" }, "enableDuckDuckGoBrowserIntegrationDesc": { - "message": "Use your Bitwarden vault when browsing with DuckDuckGo." + "message": "Naudokite savo Bitwarden saugyklą naršydami su DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { - "message": "Browser integration not supported" + "message": "Naršyklės integravimas nepalaikomas" }, "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." + "message": "Deja, bet naršyklės integravimas kol kas palaikomas tik Mac App Store versijoje." }, "browserIntegrationWindowsStoreDesc": { - "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." + "message": "Deja, bet naršyklės integravimas nepalaikomas Microsoft Store versijoje." }, "browserIntegrationLinuxDesc": { - "message": "Unfortunately browser integration is currently not supported in the linux version." + "message": "Deja, bet naršyklės integravimas nepalaikomas Linux versijoje." }, "enableBrowserIntegrationFingerprint": { - "message": "Require verification for browser integration" + "message": "Reikalauti patvirtinimo naršyklės integravimui" }, "enableBrowserIntegrationFingerprintDesc": { - "message": "Add an additional layer of security by requiring fingerprint phrase confirmation when establishing a link between your desktop and browser. This requires user action and verification each time a connection is created." + "message": "Pridėkite papildomą apsaugos sluoksnį reikalaudami piršto antspaudo frazės patvirtinimo kuriant ryšį tarp savo darbalaukio ir naršyklės. Tam reikalingas vartotojo veiksmas ir patvirtinimas kiekvieną kartą prisijungus." }, "approve": { - "message": "Approve" + "message": "Patvirtinti" }, "verifyBrowserTitle": { - "message": "Verify browser connection" + "message": "Patikrinti naršyklės jungtį" }, "verifyBrowserDesc": { - "message": "Please ensure the shown fingerprint is identical to the fingerprint showed in the browser extension." + "message": "Prašome patikrinti, ar rodomas piršto antspaudas yra identiškas piršto antspaudui rodomam naršyklės plėtinyje." }, "verifyNativeMessagingConnectionTitle": { - "message": "$APPID$ wants to connect to Bitwarden", + "message": "$APPID$ nori prisijungti prie Bitwarden", "placeholders": { "appid": { "content": "$1", @@ -1642,66 +1642,66 @@ } }, "verifyNativeMessagingConnectionDesc": { - "message": "Would you like to approve this request?" + "message": "Ar norėtumėte patvirtinti šią užklausą?" }, "verifyNativeMessagingConnectionWarning": { - "message": "If you did not initiate this request, do not approve it." + "message": "Jei jūs nepradėjote šios užklausos, jos nepatvirtinkite." }, "biometricsNotEnabledTitle": { - "message": "Biometrics not set up" + "message": "Trūksta biometrinių duomenų nustatymų" }, "biometricsNotEnabledDesc": { - "message": "Browser biometrics requires desktop biometrics to be set up in the settings first." + "message": "Pirma reikia nustatymuose nustatyti darbalaukio biometrinius duomenys, prieš juos naudojant naršyklėje." }, "personalOwnershipSubmitError": { - "message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." + "message": "Dėl įmonės politikos jums neleidžiama saugoti daiktų asmeninėje saugykloje. Pakeiskite nuosavybės parinktį į organizaciją ir pasirinkite iš galimų rinkinių." }, "hintEqualsPassword": { - "message": "Your password hint cannot be the same as your password." + "message": "Jūsų slaptažodžio užuomina negali būti lygi jūsų slaptažodžiui." }, "personalOwnershipPolicyInEffect": { - "message": "An organization policy is affecting your ownership options." + "message": "Organizacijos politika turi įtakos jūsų nuosavybės galimybėms." }, "allSends": { - "message": "All Sends", + "message": "Visi Siuntiniai", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeFile": { - "message": "File" + "message": "Failas" }, "sendTypeText": { - "message": "Text" + "message": "Tekstas" }, "searchSends": { - "message": "Search Sends", + "message": "Ieškoti Siuntinių", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { - "message": "Edit Send", + "message": "Redaguoti Siuntinį", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "myVault": { - "message": "My vault" + "message": "Mano saugykla" }, "text": { - "message": "Text" + "message": "Tekstas" }, "deletionDate": { - "message": "Deletion date" + "message": "Ištrynimo data" }, "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", + "message": "Nurodytos datos ir laiko metu Siuntinys bus visam laikui ištrintas.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { - "message": "Expiration date" + "message": "Galiojimo data" }, "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", + "message": "Jei nustatyta, prieiga prie šio Siuntinio nustos galioti pasiekus nurodytą datą ir laiką.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCount": { - "message": "Maximum access count", + "message": "Maksimalus prisijungimų skaičius", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "maxAccessCountDesc": { @@ -1907,25 +1907,25 @@ "message": "Your vault timeout exceeds the restrictions set by your organization." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "Automatinis įtraukimas" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password." + "message": "Ši organizacija turi įmonės politiką, kuri automatiškai įtrauks jus į slaptažodžio nustatymą iš naujo. Organizacijos administratoriai galės pakeisti jūsų pagrindinį slaptažodį." }, "vaultExportDisabled": { - "message": "Vault export removed" + "message": "Saugyklos eksportas panaikintas" }, "personalVaultExportPolicyInEffect": { - "message": "One or more organization policies prevents you from exporting your personal vault." + "message": "Viena ar daugiau organizacijos politikų neleidžia eksportuoti jūsų asmeninės saugyklos." }, "addAccount": { - "message": "Add account" + "message": "Pridėti paskyrą" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "Panaikinti pagrindinį slaptažodį" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "Pagrindinis slaptažodis pašalintas" }, "convertOrganizationEncryptionDesc": { "message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.", @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 5d75ee2208d..b82788f8d30 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB šifrētas krātuves datņu pielikumiem." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Tādas slēgtā pirmavota divpakāpju pieteikšanās iespējas kā YubiKey un Duo." }, "premiumSignUpReports": { "message": "Paroļu higiēnas, konta veselības un datu noplūžu pārskati, lai uzturētu glabātavu drošu." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Pašizvietots" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Piekļuve liegta. Nav nepieciešamo atļauju, lai skatītu šo lapu." diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 20336258846..9a72e54c6bf 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 18b448b1d1e..b133b150885 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 38e81a83bfd..6d569a89554 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 29d7954de21..294124b241b 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index f4f65b9f536..a84f7539b88 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 38e81a83bfd..369bdaef7b9 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -3,28 +3,28 @@ "message": "Bitwarden" }, "filters": { - "message": "Filters" + "message": "फिल्‍टरहरु" }, "allItems": { - "message": "All items" + "message": "सबै सामाग्रिहरु" }, "favorites": { - "message": "Favorites" + "message": "मनपर्नेहरू" }, "types": { - "message": "Types" + "message": "प्रकार" }, "typeLogin": { - "message": "Login" + "message": "लगइन" }, "typeCard": { - "message": "Card" + "message": "कार्ड" }, "typeIdentity": { - "message": "Identity" + "message": "पहिचान" }, "typeSecureNote": { - "message": "Secure note" + "message": "सुरक्षित नोट" }, "folders": { "message": "Folders" @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index a124cede5cf..13f6df5faf6 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Zelfgehost" + "selfHostedServer": { + "message": "zelfgehost" }, "accessDenied": { "message": "Toegang geweigerd. Je hebt geen toestemming om deze pagina te bekijken." diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index 46dee838abf..cd30ef54fdd 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index d6cf45a696e..355595090e0 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index ce85ae771dd..6ddad947f21 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Samodzielnie hostowany" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Odmowa dostępu. Nie masz uprawnień do przeglądania tej strony." diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 8e3aac885c3..8b4eb7dcc35 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 52188383594..49257f09d83 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opções proprietárias de verificação de dois passos, como YubiKey e Duo." }, "premiumSignUpReports": { "message": "Higiene de palavras-passe, saúde da conta e relatórios de violação de dados para manter o seu cofre seguro." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Auto-hospedado" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Acesso negado. Não tem permissão para visualizar esta página." diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index ca46af9d5d5..139e0813cc8 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 7785e84f1cf..b16eb66ac89 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -1078,7 +1078,7 @@ "message": "1 ГБ зашифрованного хранилища для вложенных файлов." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Проприетарные варианты двухэтапной аутентификации, такие как YubiKey или Duo." }, "premiumSignUpReports": { "message": "Гигиена паролей, здоровье аккаунта и отчеты об утечках данных для обеспечения безопасности вашего хранилища." @@ -1288,7 +1288,7 @@ "description": "ex. Date this password was updated" }, "exportVault": { - "message": "Экспортировать хранилище" + "message": "Экспорт хранилища" }, "fileFormat": { "message": "Формат файла" @@ -2116,7 +2116,7 @@ "message": "На ваше устройство отправлено уведомление." }, "fingerprintMatchInfo": { - "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка пальца совпадает на другом устройстве." + "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка совпадает на другом устройстве." }, "fingerprintPhraseHeader": { "message": "Фраза отпечатка" @@ -2226,7 +2226,7 @@ "message": "Обнаружен слабый пароль, найденный в утечке данных. Используйте надежный и уникальный пароль для защиты вашего аккаунта. Вы уверены, что хотите использовать этот пароль?" }, "checkForBreaches": { - "message": "Проверьте известные случаи утечки данных для этого пароля" + "message": "Проверять известные случаи утечки данных для этого пароля" }, "important": { "message": "Важно:" @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Собственный хостинг" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Доступ запрещен. У вас нет разрешения на просмотр этой страницы." diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index e2e5347aeb9..9d8a1428da0 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 41411606590..7277dfca0b8 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB šifrovaného úložiska." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Proprietárne možnosti dvojstupňového prihlásenia ako napríklad YubiKey a Duo." }, "premiumSignUpReports": { "message": "Správy o sile hesla, zabezpečení účtov a únikoch dát ktoré vám pomôžu udržať vaše kontá v bezpečí." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Vlastný hosting" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Prístup zamietnutý. Nemáte oprávnenie na zobrazenie tejto stránky." diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 089da060600..f12e6f5e855 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index b5712de8625..428cabcb9ee 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -1078,7 +1078,7 @@ "message": "1ГБ шифровано складиште за прилоге." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Приоритарне опције пријаве у два корака као што су YubiKey и Duo." }, "premiumSignUpReports": { "message": "Извештаји о хигијени лозинки, здравственом стању налога и кршењу података да бисте заштитили сеф." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Личан хостинг" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Одбијен приступ. Немате дозволу да видите ову страницу." diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index b224bb083c5..012566d49fc 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 38e81a83bfd..6d569a89554 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 32f10e27404..f35de19ff34 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index ff5185c7141..ae8cc13e1bd 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Barındırılan" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Erişim engellendi. Bu sayfayı görüntüleme iznine sahip değilsiniz." diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 09a7ec512bc..cf119fc5b07 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -67,7 +67,7 @@ "message": "Вкладення" }, "viewItem": { - "message": "Перегляд запису" + "message": "Переглянути запис" }, "name": { "message": "Назва" @@ -753,7 +753,7 @@ "message": "Нова тека" }, "view": { - "message": "Перегляд" + "message": "Переглянути" }, "account": { "message": "Обліковий запис" @@ -1078,7 +1078,7 @@ "message": "1 ГБ зашифрованого сховища для файлів." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Додаткові можливості двоетапної авторизації, як-от YubiKey та Duo." }, "premiumSignUpReports": { "message": "Гігієна паролів, здоров'я облікового запису, а також звіти про вразливості даних, щоб зберігати ваше сховище в безпеці." @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Власне розміщення" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Доступ заборонено. У вас немає дозволу на перегляд цієї сторінки." diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index dcbedbe2938..51577198cf9 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "Access denied. You do not have permission to view this page." diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index ff74b521eed..a8da710ca61 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1078,7 +1078,7 @@ "message": "1 GB 文件附件加密存储。" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "专有的两步登录选项,如 YubiKey 和 Duo。" }, "premiumSignUpReports": { "message": "密码健康、账户体检以及数据泄露报告,保障您的密码库安全。" @@ -2116,13 +2116,13 @@ "message": "通知已发送到您的设备。" }, "fingerprintMatchInfo": { - "message": "请确保您的密码库已解锁,并且指纹短语与其他设备匹配。" + "message": "请确保您的密码库已解锁,并且指纹短语与其他设备上的相匹配。" }, "fingerprintPhraseHeader": { "message": "指纹短语" }, "needAnotherOption": { - "message": "设备登录必须在 Bitwarden 应用程序的设置中设启用。需要其他选项吗?" + "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他登录选项吗?" }, "viewAllLoginOptions": { "message": "查看所有登录选项" @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "自托管" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "访问被拒绝。您没有权限查看此页面。" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 1de910b1b44..27d9993f543 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -2286,8 +2286,8 @@ "euDomain": { "message": "bitwarden.eu" }, - "selfHosted": { - "message": "自建" + "selfHostedServer": { + "message": "self-hosted" }, "accessDenied": { "message": "拒絕存取。您沒有檢視此頁面的權限。" From 1d667c3b3f31c5760410d5070468d770ad199d25 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:27:26 +0000 Subject: [PATCH 112/135] Autosync the updated translations (#6228) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 4 +- apps/browser/src/_locales/az/messages.json | 6 +- apps/browser/src/_locales/be/messages.json | 4 +- apps/browser/src/_locales/bg/messages.json | 6 +- apps/browser/src/_locales/bn/messages.json | 4 +- apps/browser/src/_locales/bs/messages.json | 4 +- apps/browser/src/_locales/ca/messages.json | 50 +++---- apps/browser/src/_locales/cs/messages.json | 4 +- apps/browser/src/_locales/cy/messages.json | 130 +++++++++--------- apps/browser/src/_locales/da/messages.json | 4 +- apps/browser/src/_locales/de/messages.json | 4 +- apps/browser/src/_locales/el/messages.json | 100 +++++++------- apps/browser/src/_locales/en_GB/messages.json | 4 +- apps/browser/src/_locales/en_IN/messages.json | 4 +- apps/browser/src/_locales/es/messages.json | 4 +- apps/browser/src/_locales/et/messages.json | 4 +- apps/browser/src/_locales/eu/messages.json | 4 +- apps/browser/src/_locales/fa/messages.json | 4 +- apps/browser/src/_locales/fi/messages.json | 10 +- apps/browser/src/_locales/fil/messages.json | 4 +- apps/browser/src/_locales/fr/messages.json | 4 +- apps/browser/src/_locales/gl/messages.json | 4 +- apps/browser/src/_locales/he/messages.json | 4 +- apps/browser/src/_locales/hi/messages.json | 4 +- apps/browser/src/_locales/hr/messages.json | 4 +- apps/browser/src/_locales/hu/messages.json | 4 +- apps/browser/src/_locales/id/messages.json | 4 +- apps/browser/src/_locales/it/messages.json | 6 +- apps/browser/src/_locales/ja/messages.json | 4 +- apps/browser/src/_locales/ka/messages.json | 4 +- apps/browser/src/_locales/km/messages.json | 4 +- apps/browser/src/_locales/kn/messages.json | 4 +- apps/browser/src/_locales/ko/messages.json | 4 +- apps/browser/src/_locales/lt/messages.json | 4 +- apps/browser/src/_locales/lv/messages.json | 4 +- apps/browser/src/_locales/ml/messages.json | 4 +- apps/browser/src/_locales/mr/messages.json | 10 +- apps/browser/src/_locales/my/messages.json | 4 +- apps/browser/src/_locales/nb/messages.json | 4 +- apps/browser/src/_locales/ne/messages.json | 4 +- apps/browser/src/_locales/nl/messages.json | 4 +- apps/browser/src/_locales/nn/messages.json | 4 +- apps/browser/src/_locales/or/messages.json | 4 +- apps/browser/src/_locales/pl/messages.json | 4 +- apps/browser/src/_locales/pt_BR/messages.json | 4 +- apps/browser/src/_locales/pt_PT/messages.json | 6 +- apps/browser/src/_locales/ro/messages.json | 4 +- apps/browser/src/_locales/ru/messages.json | 10 +- apps/browser/src/_locales/si/messages.json | 4 +- apps/browser/src/_locales/sk/messages.json | 6 +- apps/browser/src/_locales/sl/messages.json | 4 +- apps/browser/src/_locales/sr/messages.json | 6 +- apps/browser/src/_locales/sv/messages.json | 4 +- apps/browser/src/_locales/te/messages.json | 4 +- apps/browser/src/_locales/th/messages.json | 4 +- apps/browser/src/_locales/tr/messages.json | 4 +- apps/browser/src/_locales/uk/messages.json | 10 +- apps/browser/src/_locales/vi/messages.json | 4 +- apps/browser/src/_locales/zh_CN/messages.json | 8 +- apps/browser/src/_locales/zh_TW/messages.json | 4 +- apps/browser/store/locales/el/copy.resx | 2 +- 61 files changed, 275 insertions(+), 275 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index cf6bf851dd0..6bb0efb8fd3 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "إصدار الخادم" }, - "selfHosted": { - "message": "استضافة ذاتية" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 283b03a17a9..4ee8ab8edaf 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -796,7 +796,7 @@ "message": "Fayl qoşmaları üçün 1 GB şifrələnmiş saxlama sahəsi" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "YubiKey və Duo kimi mülkiyyətçi iki addımlı giriş seçimləri." }, "ppremiumSignUpReports": { "message": "Anbarınızın təhlükəsiyini təmin etmək üçün parol gigiyenası, hesab sağlamlığı və verilənlərin pozulması hesabatları." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server Versiyası" }, - "selfHosted": { - "message": "Öz-özünə sahiblik edən" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Üçüncü tərəf" diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 6aada06265d..e57ea2eff54 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Версія сервера" }, - "selfHosted": { - "message": "Уласнае размяшчэнне" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Іншы пастаўшчык" diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index cf9ec1b8ed9..9e4d696193e 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -796,7 +796,7 @@ "message": "1 GB пространство за файлове, които се шифрират." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Частно двустепенно удостоверяване чрез YubiKey и Duo." }, "ppremiumSignUpReports": { "message": "Проверки в списъците с публикувани пароли, проверка на регистрациите и доклади за пробивите в сигурността, което спомага трезорът ви да е допълнително защитен." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Версия на сървъра" }, - "selfHosted": { - "message": "Собствен хостинг" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 7d7151b58f6..261ceea180d 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 657ce243606..5b27e7186d4 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index fbe9e33c65d..7a7edc3d896 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -339,7 +339,7 @@ "message": "Altres" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Configura un mètode de desbloqueig per canviar l'acció del temps d'espera de la caixa forta." }, "rateExtension": { "message": "Valora aquesta extensió" @@ -634,10 +634,10 @@ "message": "Actualitza" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "Desbloquegeu la vostra caixa forta de Bitwarden per completar la sol·licitud d'emplenament automàtic." }, "notificationUnlock": { - "message": "Unlock" + "message": "Desbloqueja" }, "enableContextMenuItem": { "message": "Mostra les opcions del menú contextual" @@ -1606,10 +1606,10 @@ "message": "La biometria del navegador no és compatible amb aquest dispositiu." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "La biometria ha fallat" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "La biometria no es pot completar, considereu utilitzar una contrasenya mestra o tancar la sessió. Si això continua, poseu-vos en contacte amb el servei d'assistència de Bitwarden." }, "nativeMessaginPermissionErrorTitle": { "message": "No s'ha proporcionat el permís" @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Versió del servidor" }, - "selfHosted": { - "message": "Autoallotjat" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Tercers" @@ -2153,7 +2153,7 @@ "message": "S'ha enviat una notificació al vostre dispositiu." }, "loginInitiated": { - "message": "Login initiated" + "message": "S'ha iniciat la sessió" }, "exposedMasterPassword": { "message": "Contrasenya mestra exposada" @@ -2234,34 +2234,34 @@ } }, "loggingInOn": { - "message": "Logging in on" + "message": "Inici de sessió en" }, "opensInANewWindow": { "message": "S'obri en una finestra nova" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Cal l'aprovació del dispositiu. Seleccioneu una opció d'aprovació a continuació:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Recorda aquest dispositiu" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Desmarqueu si utilitzeu un dispositiu públic" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Aproveu des d'un altre dispositiu vostre" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Sol·liciteu l'aprovació de l'administrador" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Aprova amb contrasenya mestra" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Es requereix un identificador SSO de l'organització." }, "eu": { - "message": "EU", + "message": "UE", "description": "European Union" }, "usDomain": { @@ -2280,28 +2280,28 @@ "message": "Mostra" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Compte creat correctament!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "S'ha sol·licitat l'aprovació de l'administrador" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "La vostra sol·licitud s'ha enviat a l'administrador." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Se us notificarà una vegada aprovat." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Teniu problemes per iniciar la sessió?" }, "loginApproved": { - "message": "Login approved" + "message": "S'ha aprovat l'inici de sessió" }, "userEmailMissing": { - "message": "User email missing" + "message": "Falta el correu electrònic de l'usuari" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Dispositiu de confiança" }, "inputRequired": { "message": "L'entrada és obligatòria." diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index a7bca0d78eb..253aab484b7 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Verze serveru" }, - "selfHosted": { - "message": "Vlastní hosting" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Tretí strana" diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 043a0fffead..7861a5e758b 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -7,7 +7,7 @@ "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "A secure and free password manager for all of your devices.", + "message": "Rheolydd cyfrineiriau diogel a rhad ac am ddim ar gyfer eich holl ddyfeisiau.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -29,7 +29,7 @@ "message": "Cau" }, "submit": { - "message": "Submit" + "message": "Cyflwyno" }, "emailAddress": { "message": "Cyfeiriad ebost" @@ -227,10 +227,10 @@ "message": "Cell we Bitwarden" }, "importItems": { - "message": "Import items" + "message": "Mewnforio eitemau" }, "select": { - "message": "Select" + "message": "Dewis" }, "generatePassword": { "message": "Cynhyrchu cyfrinair" @@ -345,7 +345,7 @@ "message": "Rate the extension" }, "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" + "message": "Ystyriwch ein helpu ni gydag adolygiad da!" }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." @@ -360,7 +360,7 @@ "message": "Datgloi" }, "loggedInAsOn": { - "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "message": "Wedi mewngofnodi gyda $EMAIL$ ar $HOSTNAME$.", "placeholders": { "email": { "content": "$1", @@ -379,7 +379,7 @@ "message": "Cloi'r gell" }, "lockNow": { - "message": "Lock now" + "message": "Cloi nawr" }, "immediately": { "message": "ar unwaith" @@ -427,7 +427,7 @@ "message": "Diogelwch" }, "errorOccurred": { - "message": "An error has occurred" + "message": "Bu gwall" }, "emailRequired": { "message": "Mae angen cyfeiriad ebost." @@ -513,13 +513,13 @@ "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" }, "editedFolder": { - "message": "Folder saved" + "message": "Ffolder wedi'i chadw" }, "deleteFolderConfirmation": { "message": "Are you sure you want to delete this folder?" }, "deletedFolder": { - "message": "Folder deleted" + "message": "Ffolder wedi'i dileu" }, "gettingStartedTutorial": { "message": "Getting started tutorial" @@ -534,7 +534,7 @@ "message": "Syncing failed" }, "passwordCopied": { - "message": "Password copied" + "message": "Cyfrinair wedi'i gopïo" }, "uri": { "message": "URI" @@ -553,10 +553,10 @@ "message": "URI newydd" }, "addedItem": { - "message": "Item added" + "message": "Eitem wedi'i hychwanegu" }, "editedItem": { - "message": "Item saved" + "message": "Eitem wedi'i chadw" }, "deleteItemConfirmation": { "message": "Ydych chi wir eisiau anfon i'r sbwriel?" @@ -565,13 +565,13 @@ "message": "Anfonwyd yr eitem i'r sbwriel" }, "overwritePassword": { - "message": "Overwrite password" + "message": "Trosysgrifo'r cyfrinair" }, "overwritePasswordConfirmation": { "message": "Are you sure you want to overwrite the current password?" }, "overwriteUsername": { - "message": "Overwrite username" + "message": "Trosysgrifo'r enw defnyddiwr" }, "overwriteUsernameConfirmation": { "message": "Are you sure you want to overwrite the current username?" @@ -608,7 +608,7 @@ "message": "List identity items on the Tab page for easy auto-fill." }, "clearClipboard": { - "message": "Clear clipboard", + "message": "Clirio'r clipfwrdd", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { @@ -637,7 +637,7 @@ "message": "Unlock your Bitwarden vault to complete the auto-fill request." }, "notificationUnlock": { - "message": "Unlock" + "message": "Datgloi" }, "enableContextMenuItem": { "message": "Show context menu options" @@ -711,7 +711,7 @@ "message": "Rhannu" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "Symudwyd $ITEMNAME$ i $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -751,10 +751,10 @@ "message": "Attachment deleted" }, "newAttachment": { - "message": "Add new attachment" + "message": "Ychwanegu atodiad newydd" }, "noAttachments": { - "message": "No attachments." + "message": "Dim atodiadau." }, "attachmentSaved": { "message": "Attachment saved" @@ -763,7 +763,7 @@ "message": "Ffeil" }, "selectFile": { - "message": "Select a file" + "message": "Dewis ffeil" }, "maxFileSize": { "message": "Maximum file size is 500 MB." @@ -787,40 +787,40 @@ "message": "Adnewyddu'ch aelodaeth" }, "premiumNotCurrentMember": { - "message": "You are not currently a Premium member." + "message": "Does gennych chi ddim aeloaeth uwch ar hyn o bryd." }, "premiumSignUpAndGet": { - "message": "Sign up for a Premium membership and get:" + "message": "Cofrestrwch ar gyfer aelodaeth uwch i gael:" }, "ppremiumSignUpStorage": { - "message": "1 GB encrypted storage for file attachments." + "message": "Storfa 1GB wedi'i hamgryptio ar gyfer atodiadau ffeiliau." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Dewisiadau mewngofnodi dau gam perchenogol megis YubiKey a Duo." }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, "ppremiumSignUpTotp": { - "message": "TOTP verification code (2FA) generator for logins in your vault." + "message": "Cynhyrchydd codau dilysu TOTP (2FA) ar gyfer manylion mewngofnodi yn eich cell." }, "ppremiumSignUpSupport": { - "message": "Priority customer support." + "message": "Cymorth wedi'i flaenoriaethu." }, "ppremiumSignUpFuture": { "message": "All future Premium features. More coming soon!" }, "premiumPurchase": { - "message": "Purchase Premium" + "message": "Prynu aelodaeth uwch" }, "premiumPurchaseAlert": { "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" }, "premiumCurrentMember": { - "message": "You are a Premium member!" + "message": "Mae gennych aelodaeth uwch!" }, "premiumCurrentMemberThanks": { - "message": "Thank you for supporting Bitwarden." + "message": "Diolch am gefnogi Bitwarden." }, "premiumPrice": { "message": "Hyn oll am $PRICE$ y flwyddyn!", @@ -844,10 +844,10 @@ "message": "Ask for biometrics on launch" }, "premiumRequired": { - "message": "Premium required" + "message": "Mae angen aelodaeth uwch" }, "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." + "message": "Mae angen aelodaeth uwch i ddefnyddio'r nodwedd hon." }, "enterVerificationCodeApp": { "message": "Enter the 6 digit verification code from your authenticator app." @@ -904,7 +904,7 @@ "message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." }, "twoStepOptions": { - "message": "Two-step login options" + "message": "Dewisiadau mewngofnodi dau gam" }, "recoveryCodeDesc": { "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers from your account." @@ -1033,7 +1033,7 @@ "message": "Copy value" }, "value": { - "message": "Value" + "message": "Gwerth" }, "newCustomField": { "message": "Maes addasedig newydd" @@ -1048,7 +1048,7 @@ "message": "Hidden" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "Gwerth Boole" }, "cfTypeLinked": { "message": "Linked", @@ -1134,7 +1134,7 @@ "message": "Cod diogelwch" }, "ex": { - "message": "ex." + "message": "engh." }, "title": { "message": "Teitl" @@ -1297,7 +1297,7 @@ "message": "Starts with" }, "regEx": { - "message": "Regular expression", + "message": "Mynegiant rheolaidd", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { @@ -1427,7 +1427,7 @@ "message": "Vault timeout action" }, "lock": { - "message": "Lock", + "message": "Cloi", "description": "Verb form: to make secure or inaccesible by" }, "trash": { @@ -1438,13 +1438,13 @@ "message": "Chwilio drwy'r sbwriel" }, "permanentlyDeleteItem": { - "message": "Permanently delete item" + "message": "Dileu'r eitem yn barhaol" }, "permanentlyDeleteItemConfirmation": { "message": "Are you sure you want to permanently delete this item?" }, "permanentlyDeletedItem": { - "message": "Item permanently deleted" + "message": "Eitem wedi'i dileu'n barhaol" }, "restoreItem": { "message": "Adfer yr eitem" @@ -1546,7 +1546,7 @@ "message": "Terms of Service and Privacy Policy have not been acknowledged." }, "termsOfService": { - "message": "Terms of Service" + "message": "Telerau gwasanaeth" }, "privacyPolicy": { "message": "Polisi preifatrwydd" @@ -1671,7 +1671,7 @@ "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "expired": { - "message": "Expired" + "message": "Wedi dod i ben" }, "pendingDeletion": { "message": "Pending deletion" @@ -1744,10 +1744,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "oneDay": { - "message": "1 day" + "message": "1 diwrnod" }, "days": { - "message": "$DAYS$ days", + "message": "$DAYS$ o ddyddiau", "placeholders": { "days": { "content": "$1", @@ -1854,7 +1854,7 @@ "message": "There was an error saving your deletion and expiration dates." }, "hideEmail": { - "message": "Hide my email address from recipients." + "message": "Cuddio fy nghyfeiriad ebost rhag derbynwyr." }, "sendOptionsPolicyInEffect": { "message": "One or more organization policies are affecting your Send options." @@ -2007,10 +2007,10 @@ "message": "Regenerate username" }, "generateUsername": { - "message": "Generate username" + "message": "Cynhyrchu enw defnyddiwr" }, "usernameType": { - "message": "Username type" + "message": "Math o enw defnyddiwr" }, "plusAddressedEmail": { "message": "Plus addressed email", @@ -2026,10 +2026,10 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Hap" }, "randomWord": { - "message": "Random word" + "message": "Gair ar hap" }, "websiteName": { "message": "Website name" @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" @@ -2129,7 +2129,7 @@ "message": "New around here?" }, "rememberEmail": { - "message": "Remember email" + "message": "Cofio'r ebost" }, "loginWithDevice": { "message": "Mewngofnodi â dyfais" @@ -2165,19 +2165,19 @@ "message": "Weak and Exposed Master Password" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + "message": "Cyfrinair gwan a gafodd ei ganfod mewn achos o ddatgelu data. Defnyddiwch gyfrinair cryf ac unigryw i ddiogelu eich cyfrif. Ydych chi wir eisiau defnyddio cyfrinair sydd wedi'i ddatgelu?" }, "checkForBreaches": { - "message": "Check known data breaches for this password" + "message": "Chwilio am achosion o ddatgelu data sy'n cynnwys y cyfrinair hwn" }, "important": { "message": "Pwysig:" }, "masterPasswordHint": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "Allwch chi ddim adfer eich prif gyfrinair os caiff ei anghofio!" }, "characterMinimum": { - "message": "$LENGTH$ character minimum", + "message": "Isafswm o $LENGTH$ nod", "placeholders": { "length": { "content": "$1", @@ -2243,7 +2243,7 @@ "message": "Device approval required. Select an approval option below:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Cofio'r ddyfais hon" }, "uncheckIfPublicDevice": { "message": "Uncheck if using a public device" @@ -2261,7 +2261,7 @@ "message": "Organization SSO identifier is required." }, "eu": { - "message": "EU", + "message": "UE", "description": "European Union" }, "usDomain": { @@ -2310,7 +2310,7 @@ "message": "required" }, "search": { - "message": "Search" + "message": "Chwilio" }, "inputMinLength": { "message": "Input must be at least $COUNT$ characters long.", @@ -2377,19 +2377,19 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Dewis --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Teipiwch i hidlo --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Yn nôl dewisiadau..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Heb ganfod eitemau" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Clirio'r cyfan" }, "plusNMore": { "message": "+ $QUANTITY$ more", @@ -2401,7 +2401,7 @@ } }, "submenu": { - "message": "Submenu" + "message": "Is-ddewislen" }, "toggleCollapse": { "message": "Toggle collapse", diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index d2ddc3381b5..df94e9aab71 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Selv-hostet" + "selfHostedServer": { + "message": "selv-hostet" }, "thirdParty": { "message": "Tredjepart" diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 500329ab9ca..7dcdbc3c51f 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server-Version" }, - "selfHosted": { - "message": "Selbst gehostet" + "selfHostedServer": { + "message": "selbst gehostet" }, "thirdParty": { "message": "Drittanbieter" diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index e168fd93576..1e377c35d0d 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -339,7 +339,7 @@ "message": "Άλλες" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Ρυθμίστε μια μέθοδο ξεκλειδώματος για να αλλάξετε την ενέργεια χρονικού ορίου θησαυ/κιου." }, "rateExtension": { "message": "Βαθμολογήστε την επέκταση" @@ -796,7 +796,7 @@ "message": "1 GB κρυπτογραφημένο αποθηκευτικό χώρο για συνημμένα αρχεία." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Πρόσθετες επιλογές σύνδεσης δύο βημάτων, όπως το YubiKey και το Duo." }, "ppremiumSignUpReports": { "message": "Ασφάλεια κωδικών, υγεία λογαριασμού και αναφορές παραβίασης δεδομένων για να διατηρήσετε ασφαλές το vault σας." @@ -1438,7 +1438,7 @@ "message": "Αναζήτηση Κάδου" }, "permanentlyDeleteItem": { - "message": "Μόνιμη Διαγραφή Αντικειμένου" + "message": "Οριστική διαγραφή αντικειμένου" }, "permanentlyDeleteItemConfirmation": { "message": "Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα αυτό το στοιχείο;" @@ -1471,13 +1471,13 @@ "message": "Προειδοποίηση: Αυτή είναι μια μη ασφαλή σελίδα HTTP και οποιαδήποτε πληροφορία υποβάλλετε μπορεί να γίνει ορατή και επεμβάσιμη από άλλους. Αυτή η σύνδεση αποθηκεύτηκε αρχικά σε μια ασφαλή (HTTPS) σελίδα." }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "Θέλετε ακόμα να συμπληρώσετε αυτή τη σύνδεση;" }, "autofillIframeWarning": { "message": "Η φόρμα φιλοξενείται από διαφορετικό τομέα (domain) από το λινκ (uri) της αποθηκευμένης σύνδεσης σας (login). Επιλέξτε OK για αυτόματη συμπλήρωση, ή Ακύρωση για να σταματήσετε." }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "Για να αποτρέψετε αυτή την προειδοποίηση στο μέλλον, αποθηκεύστε αυτό το URI, $HOSTNAME$, στο στοιχείο σύνδεσης Bitwarden σας για αυτόν τον ιστότοπο.", "placeholders": { "hostname": { "content": "$1", @@ -1486,13 +1486,13 @@ } }, "setMasterPassword": { - "message": "Ορισμός Κύριου Κωδικού" + "message": "Καθορισμός κύριου κωδικού" }, "currentMasterPass": { "message": "Τρέχων Κύριος Κωδικός" }, "newMasterPass": { - "message": "Νέος Κύριος Κωδικός" + "message": "Νέος κύριος κωδικός" }, "confirmNewMasterPass": { "message": "Επιβεβαίωση Νέου Κύριου Κωδικού" @@ -1606,10 +1606,10 @@ "message": "Τα βιομετρικά στοιχεία του προγράμματος περιήγησης δεν υποστηρίζονται σε αυτήν τη συσκευή." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "Ο βιομετρικός έλεγχος απέτυχε" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "Τα βιομετρικά δεν μπόρεσαν να ολοκληρωθούν, σκεφτείτε να χρησιμοποιήσετε έναν κύριο κωδικό πρόσβασης ή να αποσυνδεθείτε. Αν αυτό εξακολουθεί να συμβαίνει, παρακαλώ επικοινωνήστε με την υποστήριξη της Bitwarden." }, "nativeMessaginPermissionErrorTitle": { "message": "Δεν Έχει Χορηγηθεί Άδεια" @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Έκδοση διακομιστή" }, - "selfHosted": { - "message": "Αυτο-φιλοξενείται" + "selfHostedServer": { + "message": "αυτο-φιλοξενούμενο" }, "thirdParty": { "message": "Τρίτο μέρος" @@ -2153,7 +2153,7 @@ "message": "Μια ειδοποίηση έχει σταλεί στη συσκευή σας." }, "loginInitiated": { - "message": "Login initiated" + "message": "Η σύνδεση ξεκίνησε" }, "exposedMasterPassword": { "message": "Εκτεθειμένος Κύριος Κωδικός Πρόσβασης" @@ -2240,28 +2240,28 @@ "message": "Ανοίγει σε νέο παράθυρο" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Απαιτείται έγκριση συσκευής. Επιλέξτε μια επιλογή έγκρισης παρακάτω:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Απομνημόνευση αυτής της συσκευής" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Αποεπιλέξτε αν γίνεται χρήση δημόσιας συσκευής" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Έγκριση από άλλη συσκευή σας" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Αίτηση έγκρισης διαχειριστή" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Έγκριση με τον κύριο κωδικό" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Απαιτείται αναγνωριστικό οργανισμού SSO." }, "eu": { - "message": "EU", + "message": "ΕΕ", "description": "European Union" }, "usDomain": { @@ -2274,46 +2274,46 @@ "message": "Δεν επιτρέπεται η πρόσβαση. Δεν έχετε άδεια για να δείτε αυτή τη σελίδα." }, "general": { - "message": "General" + "message": "Γενικά" }, "display": { - "message": "Display" + "message": "Εμφάνιση" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Επιτυχής δημιουργία λογαριασμού!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Ζητήθηκε έγκριση διαχειριστή" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Το αίτημά σας εστάλη στον διαχειριστή σας." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Θα ειδοποιηθείτε μόλις εγκριθεί." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Δεν μπορείτε να συνδεθείτε;" }, "loginApproved": { - "message": "Login approved" + "message": "Η σύνδεση εγκρίθηκε" }, "userEmailMissing": { - "message": "User email missing" + "message": "Το email του χρήστη απουσιάζει" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Αξιόπιστη συσκευή" }, "inputRequired": { - "message": "Input is required." + "message": "Απαιτείται εισαγωγή." }, "required": { - "message": "required" + "message": "απαιτείται" }, "search": { - "message": "Search" + "message": "Αναζήτηση" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Η καταχώρηση πρέπει να είναι τουλάχιστον $COUNT$ χαρακτήρες.", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2322,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Η καταχώρηση δεν πρέπει να υπερβαίνει τους $COUNT$ χαρακτήρες σε μήκος.", "placeholders": { "count": { "content": "$1", @@ -2331,7 +2331,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Οι ακόλουθοι χαρακτήρες δεν επιτρέπονται: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2340,7 +2340,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Η τιμή καταχώρησης πρέπει να είναι τουλάχιστον $MIN$", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2349,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Η τιμή καταχώρησης δεν πρέπει να υπερβαίνει το $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2358,17 +2358,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 ή περισσότερα email δεν είναι έγκυρα" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Η καταχώρηση δεν πρέπει να περιέχει μόνο κενά.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Η καταχώρηση δεν είναι διεύθυνση email." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ Το/α παραπάνω πεδίo/α χρειάζονται την προσοχή σας.", "placeholders": { "count": { "content": "$1", @@ -2377,22 +2377,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Επιλογή --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Πληκτρολογήστε για φιλτράρισμα --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Ανάκτηση επιλογών..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Δεν βρέθηκαν αντικείμενα" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Εκκαθάριση όλων" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ περισσότερα", "placeholders": { "quantity": { "content": "$1", @@ -2401,10 +2401,10 @@ } }, "submenu": { - "message": "Submenu" + "message": "Υπομενού" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Εναλλαγή σύμπτυξης", "description": "Toggling an expand/collapse state." } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 1632b31ee03..84fee7b77c3 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index b9f2a3784c3..4bf280ce707 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server Version" }, - "selfHosted": { - "message": "Self-Hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-Party" diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index e43102b483e..50b721348c8 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Versión del servidor" }, - "selfHosted": { - "message": "Autoalojado" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Aplicaciones de terceros" diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 5fc83e82676..f9a11297795 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Serveri versioon" }, - "selfHosted": { - "message": "Enda majutatud" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Kolmanda osapoole" diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index ac54d0c4ea6..987565d609d 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Zerbitzariaren bertsioa" }, - "selfHosted": { - "message": "Ostatatze propioduna" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Hirugarrenen aplikazioak" diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index e1057038933..ebb75c9f893 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "نسخه سرور" }, - "selfHosted": { - "message": "خود میزبان" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "شخص ثالث" diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 69492afd7f4..2abd01eefa6 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -796,7 +796,7 @@ "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Omisteiset kaksivaiheisen kirjautumisen vaihtoehdot, kuten YubiKey ja Duo." }, "ppremiumSignUpReports": { "message": "Salasanahygienian, tilin terveyden ja tietovuotojen raportointitoiminnot pitävät holvisi turvassa." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Palvelimen versio" }, - "selfHosted": { - "message": "Itse ylläpidetty" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Ulkopuolinen taho" @@ -2246,7 +2246,7 @@ "message": "Muista tämä laite" }, "uncheckIfPublicDevice": { - "message": "Poista käytöstä julkisilla laitteilla" + "message": "Poista valinta julkisilla laitteilla" }, "approveFromYourOtherDevice": { "message": "Hyväksy muilta laitteiltasi" @@ -2286,7 +2286,7 @@ "message": "Hyväksyntää pyydetty ylläpidolta" }, "adminApprovalRequestSentToAdmins": { - "message": "Pyyntösi on välitetty ylläpidolle." + "message": "Pyyntösi on välitetty ylläpidollesi." }, "youWillBeNotifiedOnceApproved": { "message": "Saat ilmoituksen kun se on hyväksytty." diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index be101c80b41..3f046898751 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Bersyon ng server" }, - "selfHosted": { - "message": "Auto-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Ika-tatlong-partido" diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index b554c619a5f..d9dcd906c10 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Version du serveur" }, - "selfHosted": { - "message": "Auto-hébergé" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Tierce partie" diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 752935ce81a..d199a2e8db9 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index d5c465e68bf..e1d89271f21 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index db0fefbbffb..ee296293572 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Verzija poslužitelja" }, - "selfHosted": { - "message": "Vlastiti poslužitelj" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 8bc89651d53..62a14307f6e 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Szerver verzió" }, - "selfHosted": { - "message": "Saját kiszolgáló" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Harmadik fél" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index d8f6698f4ce..5b1d144591d 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index e17ea019409..ad335ccd56c 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -796,7 +796,7 @@ "message": "1 GB di spazio di archiviazione criptato per gli allegati." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opzioni di verifica in due passaggi proprietarie come YubiKey e Duo." }, "ppremiumSignUpReports": { "message": "Sicurezza delle password, integrità dell'account, e rapporti su violazioni di dati per mantenere sicura la tua cassaforte." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Versione Server" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Terze parti" diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index e979237988c..629a1644510 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "サーバーのバージョン" }, - "selfHosted": { - "message": "セルフホスト" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "サードパーティー" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index a619c47eaf4..f950febf1f3 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index fd01869139d..3635ce719ba 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index f982332a503..c8ad9cff366 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index a3ba40c1fd9..b8f9c6cf12a 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Trečioji šalis" diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index cbf2c469124..dfe5405e037 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Servera versija" }, - "selfHosted": { - "message": "Pašizvietots" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Trešās puses" diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 416b35f78ec..dab1da00605 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index e12aa7f8a65..4401946ca5d 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -208,10 +208,10 @@ "message": "संकालन" }, "syncVaultNow": { - "message": "Sync vault now" + "message": "तिजोरी संकालन आता करा" }, "lastSync": { - "message": "Last sync:" + "message": "शेवटचे संकालन:" }, "passGen": { "message": "पासवर्ड जनित्र" @@ -279,7 +279,7 @@ "message": "Avoid ambiguous characters" }, "searchVault": { - "message": "Search vault" + "message": "तिजोरीत शोधा" }, "edit": { "message": "Edit" @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index e806ea0a781..5a3cb1a6675 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server Versjon" }, - "selfHosted": { - "message": "Selvbetjent" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Tredjepart" diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index f34016f70f0..7149b18ff68 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Serverversie" }, - "selfHosted": { - "message": "Zelfgehost" + "selfHostedServer": { + "message": "zelfgehost" }, "thirdParty": { "message": "van derden" diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 75f416b14e4..c2ec8abe5d0 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Wersja serwera" }, - "selfHosted": { - "message": "Samodzielnie hostowany" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Inny dostawca" diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index f8db10e49f8..474174a36e9 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Versão do servidor" }, - "selfHosted": { - "message": "Auto-hospedado" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Terceiros" diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 074ddb150e0..28362dc31ad 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -796,7 +796,7 @@ "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opções proprietárias de verificação de dois passos, como YubiKey e Duo." }, "ppremiumSignUpReports": { "message": "Higiene de palavras-passe, saúde da conta e relatórios de violação de dados para manter o seu cofre seguro." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Versão do servidor" }, - "selfHosted": { - "message": "Auto-hospedado" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "De terceiros" diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 20c44ddcca2..40c86ecf37d 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Versiune server" }, - "selfHosted": { - "message": "Autogăzduit" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Parte terță" diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 738193fb48a..df0906a0ec1 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -671,7 +671,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { - "message": "Экспортировать хранилище" + "message": "Экспорт хранилища" }, "fileFormat": { "message": "Формат файла" @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Версия сервера" }, - "selfHosted": { - "message": "Собственный хостинг" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Сторонний" @@ -2141,7 +2141,7 @@ "message": "Фраза отпечатка" }, "fingerprintMatchInfo": { - "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка пальца совпадает на другом устройстве." + "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка совпадает на другом устройстве." }, "resendNotification": { "message": "Отправить уведомление повторно" @@ -2168,7 +2168,7 @@ "message": "Обнаружен слабый пароль, найденный в утечке данных. Используйте надежный и уникальный пароль для защиты вашего аккаунта. Вы уверены, что хотите использовать этот пароль?" }, "checkForBreaches": { - "message": "Проверьте известные случаи утечки данных для этого пароля" + "message": "Проверять известные случаи утечки данных для этого пароля" }, "important": { "message": "Важно:" diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index cf3ac370c6d..1236f44e6eb 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 1b90bc94053..5778db28f79 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -796,7 +796,7 @@ "message": "1 GB šifrovaného úložiska." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Proprietárne možnosti dvojstupňového prihlásenia ako napríklad YubiKey a Duo." }, "ppremiumSignUpReports": { "message": "Správy o sile hesla, zabezpečení účtov a únikoch dát ktoré vám pomôžu udržať vaše kontá v bezpečí." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Verzia servera" }, - "selfHosted": { - "message": "Vlastný hosting" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Tretia strana" diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index cae2464dcb1..06967ee2166 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Verzija strežnika" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index d4e5ee4f2a4..f823208d1dd 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -796,7 +796,7 @@ "message": "1ГБ шифровано складиште за прилоге." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Приоритарне опције пријаве у два корака као што су YubiKey и Duo." }, "ppremiumSignUpReports": { "message": "Извештаји о хигијени лозинки, здравственом стању налога и кршењу података да бисте заштитили сеф." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Верзија сервера" }, - "selfHosted": { - "message": "Личан хостинг" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Трећа страна" diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 7ab058680c4..2c90c1c8f86 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Serverversion" }, - "selfHosted": { - "message": "Lokalt installerad" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Tredje part" diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 6aea5876eac..43c3cc0b68e 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 7fcf7835119..97ce8ca58cc 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Server version" }, - "selfHosted": { - "message": "Self-hosted" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 4134a27d265..e32dcb4ac81 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Sunucu sürümü" }, - "selfHosted": { - "message": "Barındırılan" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Üçüncü taraf" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 2d1a0b66291..c695aaadf2e 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -285,7 +285,7 @@ "message": "Змінити" }, "view": { - "message": "Перегляд" + "message": "Переглянути" }, "noItemsInList": { "message": "Немає записів." @@ -321,7 +321,7 @@ "message": "Видалити запис" }, "viewItem": { - "message": "Перегляд запису" + "message": "Переглянути запис" }, "launch": { "message": "Перейти" @@ -796,7 +796,7 @@ "message": "1 ГБ зашифрованого сховища для файлів." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Додаткові можливості двоетапної авторизації, як-от YubiKey та Duo." }, "ppremiumSignUpReports": { "message": "Гігієна паролів, здоров'я облікового запису, а також звіти про вразливості даних, щоб зберігати ваше сховище в безпеці." @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Версія сервера" }, - "selfHosted": { - "message": "Власне розміщення" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Сторонній" diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 5d71340db52..95716b3fc36 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "Phiên bản máy chủ" }, - "selfHosted": { - "message": "Tự lưu trữ" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "Bên thứ ba" diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 1fc419d0d16..eded4b220c7 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -796,7 +796,7 @@ "message": "1 GB 文件附件加密存储。" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "专有的两步登录选项,如 YubiKey 和 Duo。" }, "ppremiumSignUpReports": { "message": "密码健康、账户体检以及数据泄露报告,保障您的密码库安全。" @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "服务器版本" }, - "selfHosted": { - "message": "自托管" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "第三方" @@ -2135,7 +2135,7 @@ "message": "设备登录" }, "loginWithDeviceEnabledInfo": { - "message": "设备登录必须在 Bitwarden 应用程序的设置中设启用。需要其他选项吗?" + "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他登录选项吗?" }, "fingerprintPhraseHeader": { "message": "指纹短语" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 68eb917021e..96bdfaaa4df 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -2092,8 +2092,8 @@ "serverVersion": { "message": "伺服器版本" }, - "selfHosted": { - "message": "自我裝載" + "selfHostedServer": { + "message": "self-hosted" }, "thirdParty": { "message": "第三方" diff --git a/apps/browser/store/locales/el/copy.resx b/apps/browser/store/locales/el/copy.resx index 496118ddf6e..01def6ea5af 100644 --- a/apps/browser/store/locales/el/copy.resx +++ b/apps/browser/store/locales/el/copy.resx @@ -155,7 +155,7 @@ Ένας ασφαλής και δωρεάν διαχειριστής κωδικών για όλες τις συσκευές σας - Συγχρονίστε και αποκτήστε πρόσβαση στο vault σας από πολλές συσκευές + Συγχρονίστε και αποκτήστε πρόσβαση στο θησαυροφυλάκιό σας από πολλαπλές συσκευές Διαχειριστείτε όλες τις συνδέσεις και τους κωδικούς σας με ασφάλεια From fe354f906339e7f5cb8b6f87e16b8a2f51bbc8d0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:29:11 +0000 Subject: [PATCH 113/135] Autosync the updated translations (#6227) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 11 +-- apps/web/src/locales/ar/messages.json | 11 +-- apps/web/src/locales/az/messages.json | 29 +++--- apps/web/src/locales/be/messages.json | 73 ++++++++-------- apps/web/src/locales/bg/messages.json | 17 ++-- apps/web/src/locales/bn/messages.json | 11 +-- apps/web/src/locales/bs/messages.json | 11 +-- apps/web/src/locales/ca/messages.json | 107 +++++++++++------------ apps/web/src/locales/cs/messages.json | 11 +-- apps/web/src/locales/cy/messages.json | 11 +-- apps/web/src/locales/da/messages.json | 11 +-- apps/web/src/locales/de/messages.json | 13 ++- apps/web/src/locales/el/messages.json | 11 +-- apps/web/src/locales/en_GB/messages.json | 11 +-- apps/web/src/locales/en_IN/messages.json | 11 +-- apps/web/src/locales/eo/messages.json | 11 +-- apps/web/src/locales/es/messages.json | 15 ++-- apps/web/src/locales/et/messages.json | 11 +-- apps/web/src/locales/eu/messages.json | 13 ++- apps/web/src/locales/fa/messages.json | 13 ++- apps/web/src/locales/fi/messages.json | 53 ++++++----- apps/web/src/locales/fil/messages.json | 13 ++- apps/web/src/locales/fr/messages.json | 11 +-- apps/web/src/locales/gl/messages.json | 11 +-- apps/web/src/locales/he/messages.json | 11 +-- apps/web/src/locales/hi/messages.json | 11 +-- apps/web/src/locales/hr/messages.json | 13 ++- apps/web/src/locales/hu/messages.json | 13 ++- apps/web/src/locales/id/messages.json | 11 +-- apps/web/src/locales/it/messages.json | 15 ++-- apps/web/src/locales/ja/messages.json | 13 ++- apps/web/src/locales/ka/messages.json | 11 +-- apps/web/src/locales/km/messages.json | 11 +-- apps/web/src/locales/kn/messages.json | 11 +-- apps/web/src/locales/ko/messages.json | 11 +-- apps/web/src/locales/lv/messages.json | 13 ++- apps/web/src/locales/ml/messages.json | 11 +-- apps/web/src/locales/mr/messages.json | 11 +-- apps/web/src/locales/my/messages.json | 11 +-- apps/web/src/locales/nb/messages.json | 13 ++- apps/web/src/locales/ne/messages.json | 11 +-- apps/web/src/locales/nl/messages.json | 13 ++- apps/web/src/locales/nn/messages.json | 11 +-- apps/web/src/locales/or/messages.json | 11 +-- apps/web/src/locales/pl/messages.json | 11 +-- apps/web/src/locales/pt_BR/messages.json | 15 ++-- apps/web/src/locales/pt_PT/messages.json | 19 ++-- apps/web/src/locales/ro/messages.json | 11 +-- apps/web/src/locales/ru/messages.json | 21 ++--- apps/web/src/locales/si/messages.json | 11 +-- apps/web/src/locales/sk/messages.json | 13 ++- apps/web/src/locales/sl/messages.json | 11 +-- apps/web/src/locales/sr/messages.json | 15 ++-- apps/web/src/locales/sr_CS/messages.json | 11 +-- apps/web/src/locales/sv/messages.json | 13 ++- apps/web/src/locales/te/messages.json | 11 +-- apps/web/src/locales/th/messages.json | 11 +-- apps/web/src/locales/tr/messages.json | 13 ++- apps/web/src/locales/uk/messages.json | 21 ++--- apps/web/src/locales/vi/messages.json | 11 +-- apps/web/src/locales/zh_CN/messages.json | 19 ++-- apps/web/src/locales/zh_TW/messages.json | 13 ++- 62 files changed, 400 insertions(+), 586 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 4b80c3c2119..46c073483aa 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Beveilig u rekening deur ’n bykomende stap te vereis wanneer u aanteken." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Volgende" }, - "usFlag": { - "message": "VS-vlag" - }, - "euFlag": { - "message": "EU-vlag" - }, "selectedRegionFlag": { "message": "Gekose streekvlag" }, diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index f43798b93c1..8639957af05 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 6e037753488..18eea5284fe 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -815,7 +815,7 @@ "message": "Təsdiqləmə kodu olan e-poçtu yenidən göndər" }, "useAnotherTwoStepMethod": { - "message": "Başqa bir iki mərhələli giriş metodu istifadə edin" + "message": "Başqa bir iki addımlı giriş üsulu istifadə edin" }, "insertYubiKey": { "message": "\"YubiKey\"i kompüterinizin USB portuna taxın, daha sonra düyməsinə toxunun." @@ -827,13 +827,13 @@ "message": "Giriş edilə bilmir" }, "noTwoStepProviders": { - "message": "Bu hesabın iki mərhələli giriş özəlliyi fəaldır, ancaq, konfiqurasiya edilmiş iki mərhələli provayderlərin heç biri bu brauzer tərəfindən dəstəklənmir." + "message": "Bu hesabda iki addımlı giriş tənzimləməsi var, ancaq, konfiqurasiya edilmiş iki addımlı provayderlərin heç biri bu veb brauzer tərəfindən dəstəklənmir." }, "noTwoStepProviders2": { "message": "Zəhmət olmasa (Chrome kimi) dəstəklənən bir veb brauzer istifadə edin və/və ya veb brauzerlərə (kimlik təsdiqləyici tətbiq kimi) daha yaxşı dəstəklənən provayderlər əlavə edin." }, "twoStepOptions": { - "message": "İki mərhələli giriş seçimləri" + "message": "İki addımlı giriş seçimləri" }, "recoveryCodeDesc": { "message": "İki mərhələli təsdiqləmə provayderlərinə müraciəti itirmisiniz? Bərpa kodunuzu istifadə edərək hesabınızdakı bütün iki mərhələli provayderləri sıradan çıxara bilərsiniz." @@ -1417,23 +1417,26 @@ "message": "Domenlər güncəlləndi" }, "twoStepLogin": { - "message": "İki mərhələli giriş" + "message": "İki addımlı giriş" }, "twoStepLoginEnforcement": { - "message": "İki addımlı girişə məcbur et" + "message": "İki addımlı Giriş Məcburiyyəti" }, "twoStepLoginDesc": { "message": "Giriş edərkən əlavə bir addım tələb edərək hesabınızı qoruyun." }, - "twoStepLoginOrganizationDescStart": { - "message": "Bitwarden iki addım giriş seçimlərini aşağıdakıları istifadə edərək üzvlər üçün tətbiq et ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { - "message": "İki mərhələli giriş siyasəti" + "message": "İki addımlı Giriş Siyasəti" }, "twoStepLoginOrganizationDuoDesc": { - "message": "Duo üzərindən iki mərhələli girişi tətbiq etmək üçün aşağıdakı seçimləri istifadə edin." + "message": "Duo üzərindən İki addımlı Girişi tətbiq etmək üçün aşağıdakı seçimləri istifadə edin." }, "twoStepLoginOrganizationSsoDesc": { "message": "SSO quraşdırması etmisinizsə və ya etmək planınız varsa, İki mərhələli giriş, artıq Kimlik Provayderiniz vasitəsilə tətbiq edilmiş ola bilər." @@ -1925,7 +1928,7 @@ "message": "Fayl qoşmaları üçün 1 GB şifrələnmiş saxlama sahəsi." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "YubiKey və Duo kimi mülkiyyətçi iki addımlı giriş seçimləri." }, "premiumSignUpEmergency": { "message": "Fövqəladə vəziyyət müraciəti" @@ -7016,12 +7019,6 @@ "next": { "message": "Növbəti" }, - "usFlag": { - "message": "US bayrağı" - }, - "euFlag": { - "message": "EU bayrağı" - }, "selectedRegionFlag": { "message": "Seçilmiş bölgə bayrağı" }, diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 305dcf75092..449b220591f 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Абараніце свой уліковы запіс з дапамогай дадатковага кроку праверкі падчас уваходу." }, - "twoStepLoginOrganizationDescStart": { - "message": "Забяспечыць параметры двухэтапнага ўваходу Bitwarden для ўдзельнікаў выкарыстоўваючы ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 ГБ зашыфраванага сховішча для далучаных файлаў." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Прапрыетарныя варыянты двухэтапнага ўваходу, такія як YubiKey, FIDO U2F і Duo." }, "premiumSignUpEmergency": { "message": "Экстранны доступ" @@ -2041,7 +2044,7 @@ } }, "paymentChargedWithUnpaidSubscription": { - "message": "Your payment method will be charged for any unpaid subscriptions." + "message": "Ваш метад аплаты будзе выкарыстаны для любых неаплачаных падпісак." }, "paymentChargedWithTrial": { "message": "У ваш тарыфны план уключаны выпрабавальны перыяд на 7 дзён. У вас не будзе спагнана плата згодна з выбраным спосабам аплаты пакуль не завяршыцца выпрабавальны перыяд. Вы можаце скасаваць яго ў любы момант." @@ -3540,7 +3543,7 @@ "message": "Выберыце дзеянне, якое неабходна выканаць пасля завяршэння часу чакання сховішча." }, "vaultTimeoutLogoutDesc": { - "message": "Choose when your vault will be logged out." + "message": "Выберыце дзеянне пры якім адбудзецца выхад са сховішча." }, "oneMinute": { "message": "1 хвіліна" @@ -6842,58 +6845,58 @@ "message": "Абнавіце налады KDF" }, "loginInitiated": { - "message": "Login initiated" + "message": "Ініцыяваны ўваход" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Патрабуецца ўхваленне прылады. Выберыце параметры ўхвалення ніжэй:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Запомніць гэту прыладу" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Здыміце пазнаку, калі выкарыстоўваеце агульнадаступную прыладу" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Ухваліць з іншай вашай прылады" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Запытаць ухваленне адміністратара" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Ухваліць з дапамогай асноўнага пароля" }, "trustedDeviceEncryption": { - "message": "Trusted device encryption" + "message": "Шыфраванне даверанай прылады" }, "trustedDevices": { "message": "Давераныя прылады" }, "memberDecryptionOptionTdeDescriptionPartOne": { - "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", + "message": "Пасля аўтэнтыфікацыі ўдзельнікі расшыфроўваюць даныя сховішча з выкарыстаннем ключа, які захоўваецца на іх прыладах.", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkOne": { - "message": "single organization", + "message": "адзіная арганізацыя", "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.'" }, "memberDecryptionOptionTdeDescriptionPartTwo": { - "message": "policy,", + "message": "палітыка", "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": { - "message": "policy, and", + "message": "палітыка і", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkThree": { - "message": "account recovery administration", + "message": "адміністраванне аднаўлення ўліковага запісу", "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.'" }, "memberDecryptionOptionTdeDescriptionPartFour": { - "message": "policy with automatic enrollment will turn on when this option is used.", + "message": "палітыка з аўтаматычнай рэгістрацыяй уключаецца пры выкарыстанні гэтага параметра.", "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.'" }, "notFound": { @@ -6984,7 +6987,7 @@ "message": "Выдаленне ўдзельнікаў у якіх адсутнічае асноўны пароль і для якіх ён не быў прызначаны папярэдне можа стаць прычынай абмежавання доступу да іх уліковага запісу." }, "approvedAuthRequest": { - "message": "Approved device for $ID$.", + "message": "Ухваленне прылады для $ID$.", "placeholders": { "id": { "content": "$1", @@ -6993,7 +6996,7 @@ } }, "rejectedAuthRequest": { - "message": "Denied device for $ID$.", + "message": "Адхіленне прылады для $ID$.", "placeholders": { "id": { "content": "$1", @@ -7002,7 +7005,7 @@ } }, "requestedDeviceApproval": { - "message": "Requested device approval." + "message": "Запыт ухвалення прылады." }, "startYour7DayFreeTrialOfBitwardenFor": { "message": "Пачніце 7-дзённую бясплатную пробную версію для $ORG$", @@ -7016,38 +7019,32 @@ "next": { "message": "Далей" }, - "usFlag": { - "message": "Сцяг ЗША" - }, - "euFlag": { - "message": "Сцяг ЕС" - }, "selectedRegionFlag": { "message": "Сцяг выбранага рэгіёна" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Уліковы запіс паспяхова створаны!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Патрабуецца ўхваленне адміністратара" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Ваш запыт адпраўлены адміністратару." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Вы атрымаеце апавяшчэння пасля яго ўхвалення." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Праблемы з уваходам?" }, "loginApproved": { - "message": "Login approved" + "message": "Уваход ухвалены" }, "userEmailMissing": { - "message": "User email missing" + "message": "Адсутнічае электронная пошта карыстальніка" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Давераная прылада" }, "sendsNoItemsTitle": { "message": "Няма актыўных Send'аў", @@ -7160,7 +7157,7 @@ "message": "Максімальны патэнцыйны кошт сэрвісных уліковых запісаў" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Вы ўвайшлі!" }, "smBetaEndedDesc": { "message": "Перыяд бэта-тэсціравання менеджара сакрэтаў завершыцца $BETA_ENDING_DATE$. Дадайце менеджар сакрэтаў у сваю платную падпіску і захавайце доступ да даных (засталося дзён: $DAYS$). Звяжыцеся з нашай службай падтрымкі, каб дадаць менеджар сакрэтаў у сваю платную падпіску.", diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 9b32d828818..4cf667a626c 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Допълнителна защита на абонамента чрез изискването на допълнително действие при вписване." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -1925,7 +1928,7 @@ "message": "1 GB пространство за файлове, които се шифрират." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Частно двустепенно удостоверяване чрез YubiKey и Duo." }, "premiumSignUpEmergency": { "message": "Авариен достъп" @@ -4758,10 +4761,10 @@ "message": "Потребителят с най-големи права на достъп, който може да управлява всички настройки на доставчика, както и да има достъп до и да управлява клиентските организации." }, "serviceUser": { - "message": "Service user" + "message": "Сервизен потребител" }, "serviceUserDesc": { - "message": "Service users can access and manage all client organizations." + "message": "Сервизните потребители имат достъп до всички клиентски организации и могат да ги управляват." }, "providerInviteUserDesc": { "message": "Може да поканите потребител към доставчика си, като попълните адреса му за електронна поща, с който е регистриран в Битуорден, по-долу. В случай, че потребителят не е регистриран, той автоматично ще получи покана и да се регистрира." @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "Знаме на САЩ" - }, - "euFlag": { - "message": "Знаме на ЕС" - }, "selectedRegionFlag": { "message": "Знаме на избрания регион" }, diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index c970aacffb7..065e6482322 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index ea7b495a715..bff25d8f84c 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 498f1a56615..d3283662601 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -712,7 +712,7 @@ "message": "S'ha produït un error inesperat." }, "expirationDateError": { - "message": "Please select an expiration date that is in the future." + "message": "Seleccioneu una data de caducitat futura." }, "emailAddress": { "message": "Adreça electrònica" @@ -955,7 +955,7 @@ "message": "Copia el codi de verificació" }, "copyUuid": { - "message": "Copy UUID" + "message": "Copia UUID" }, "warning": { "message": "Advertiment" @@ -1294,19 +1294,19 @@ "message": "Error en desxifrar el fitxer exportat. La vostra clau de xifratge no coincideix amb la clau de xifratge utilitzada per exportar les dades." }, "importDestination": { - "message": "Import destination" + "message": "Importa destinació" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "Obteniu informació sobre les opcions d'importació" }, "selectImportFolder": { - "message": "Select a folder" + "message": "Selecciona una carpeta" }, "selectImportCollection": { - "message": "Select a collection" + "message": "Selecciona una col·lecció" }, "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "message": "Seleccioneu aquesta opció si voleu que el contingut del fitxer importat es desplace a $DESTINATION$", "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", "placeholders": { "destination": { @@ -1316,7 +1316,7 @@ } }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "El fitxer conté elements no assignats." }, "selectFormat": { "message": "Seleccioneu el format del fitxer d'importació" @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Protegiu el vostre compte exigint un pas addicional en iniciar sessió." }, - "twoStepLoginOrganizationDescStart": { - "message": "Apliqueu les opcions d'inici de sessió en dos passos de Bitwarden per als membres mitjançant la ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opcions propietàries de doble factor com ara YubiKey i Duo." }, "premiumSignUpEmergency": { "message": "Accés d’emergència" @@ -2041,7 +2044,7 @@ } }, "paymentChargedWithUnpaidSubscription": { - "message": "Your payment method will be charged for any unpaid subscriptions." + "message": "Es cobrarà al vostre mètode de pagament les subscripcions no pagades." }, "paymentChargedWithTrial": { "message": "El vostre pla inclou una prova gratuïta de 7 dies. El mètode de pagament no es cobrarà fins que no s'haja acabat la prova. Podeu cancel·lar-ho en qualsevol moment." @@ -3540,7 +3543,7 @@ "message": "Trieu quan es tancarà la vostra caixa forta i feu l'acció seleccionada." }, "vaultTimeoutLogoutDesc": { - "message": "Choose when your vault will be logged out." + "message": "Trieu quan es tancarà la sessió de la vostra caixa forta." }, "oneMinute": { "message": "1 minut" @@ -3631,7 +3634,7 @@ "message": "Aquest element té fitxers adjunts antics que s'han de corregir." }, "attachmentFixDescription": { - "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." + "message": "Aquest fitxer adjunt utilitza un xifratge obsolet. Seleccioneu \"Arregla\" per descarregar, tornar a xifrar i tornar a carregar el fitxer adjunt." }, "fix": { "message": "Corregeix", @@ -4939,7 +4942,7 @@ "message": "Inhabilita l'exportació de la caixa forta personal" }, "disablePersonalVaultExportDescription": { - "message": "Do not allow members to export data from their individual vault." + "message": "No permetes que els membres exporten dades des de la seua caixa forta individual." }, "vaultExportDisabled": { "message": "L'exportació de la caixa forta s'ha suprimit" @@ -5443,7 +5446,7 @@ "message": "S'està exportant la caixa forta de l’organització" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Només s'exportaran els elements de la caixa de seguretat individuals associats a $EMAIL$. Els elements de la caixa de l'organització no s'inclouran. Només s'exportarà la informació de l'element de la caixa de seguretat i no inclourà els fitxers adjunts associats.", "placeholders": { "email": { "content": "$1", @@ -5452,7 +5455,7 @@ } }, "exportingOrganizationVaultDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", + "message": "Només s'exportarà la caixa forta de l'organització associada a $ORGANIZATION$. No s'inclouran els elements de les caixes fortes individuals ni d'altres organitzacions.", "placeholders": { "organization": { "content": "$1", @@ -6842,58 +6845,58 @@ "message": "Actualitza la configuració de KDF" }, "loginInitiated": { - "message": "Login initiated" + "message": "S'ha iniciat la sessió" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Cal l'aprovació del dispositiu. Seleccioneu una opció d'aprovació a continuació:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Recorda aquest dispositiu" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Desmarqueu si utilitzeu un dispositiu públic" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Aproveu des d'un altre dispositiu vostre" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Sol·liciteu l'aprovació de l'administrador" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Aprova amb contrasenya mestra" }, "trustedDeviceEncryption": { - "message": "Trusted device encryption" + "message": "Encriptació de dispositius de confiança" }, "trustedDevices": { "message": "Dispositius de confiança" }, "memberDecryptionOptionTdeDescriptionPartOne": { - "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", + "message": "Una vegada autenticats, els membres desxifraran les dades de la caixa forta mitjançant una clau emmagatzemada al seu dispositiu. La", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkOne": { - "message": "single organization", + "message": "política d'organització", "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.'" }, "memberDecryptionOptionTdeDescriptionPartTwo": { - "message": "policy,", + "message": "única,", "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": "la política de", "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": { - "message": "policy, and", + "message": "requerida y la política", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkThree": { - "message": "account recovery administration", + "message": "d'administració de recuperació del compte", "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.'" }, "memberDecryptionOptionTdeDescriptionPartFour": { - "message": "policy with automatic enrollment will turn on when this option is used.", + "message": "amb inscripció automàtica s'activaran quan s'utilitze aquesta opció.", "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.'" }, "notFound": { @@ -6984,7 +6987,7 @@ "message": "La supressió de membres que no tenen contrasenyes mestres sense establir-ne una pot restringir l'accés al seu compte complet." }, "approvedAuthRequest": { - "message": "Approved device for $ID$.", + "message": "Dispositiu aprovat per $ID$.", "placeholders": { "id": { "content": "$1", @@ -6993,7 +6996,7 @@ } }, "rejectedAuthRequest": { - "message": "Denied device for $ID$.", + "message": "Dispositiu denegat per $ID$.", "placeholders": { "id": { "content": "$1", @@ -7002,7 +7005,7 @@ } }, "requestedDeviceApproval": { - "message": "Requested device approval." + "message": "S'ha sol·licitat l'aprovació del dispositiu." }, "startYour7DayFreeTrialOfBitwardenFor": { "message": "Inicieu la vostra prova gratuïta de 7 dies de Bitwarden per a $ORG$", @@ -7016,38 +7019,32 @@ "next": { "message": "Següent" }, - "usFlag": { - "message": "Bandera EUA" - }, - "euFlag": { - "message": "Bandera UE" - }, "selectedRegionFlag": { "message": "Bandera de la regió seleccionada" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Compte creat correctament!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "S'ha sol·licitat l'aprovació de l'administrador" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "La vostra sol·licitud s'ha enviat a l'administrador." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Se us notificarà una vegada aprovat." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Teniu problemes per iniciar la sessió?" }, "loginApproved": { - "message": "Login approved" + "message": "S'ha aprovat l'inici de sessió" }, "userEmailMissing": { - "message": "User email missing" + "message": "Falta el correu electrònic de l'usuari" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Dispositiu de confiança" }, "sendsNoItemsTitle": { "message": "No hi ha Sends actius", @@ -7118,7 +7115,7 @@ "message": "Comptes de serveis addicionals" }, "includedServiceAccounts": { - "message": "Your plan comes with $COUNT$ service accounts.", + "message": "El vostre pla inclou $COUNT$ comptes de servei.", "placeholders": { "count": { "content": "$1", @@ -7127,7 +7124,7 @@ } }, "addAdditionalServiceAccounts": { - "message": "You can add additional service accounts for $COST$ per month.", + "message": "Podeu afegir comptes de servei addicionals per $COST$ al mes.", "placeholders": { "cost": { "content": "$1", @@ -7160,10 +7157,10 @@ "message": "Cost potencial màxim del compte de servei" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Connectat!" }, "smBetaEndedDesc": { - "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "message": "La beta de l'administrador de secrets va finalitzar el dia $BETA_ENDING_DATE$. Et queden $DAYS$ dies per afegir l'administrador de secrets a la teua subscripció de pagament i mantindre l'accés a les dades de l'administrador de secrets. Contacteu amb Customer Success per afegir l'administrador de secrets a la vostra subscripció.", "placeholders": { "beta_ending_date": { "content": "$1", @@ -7176,12 +7173,12 @@ } }, "betaEnding": { - "message": "Beta Ending" + "message": "Beta final" }, "beta": { "message": "Beta" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "Ja tens un compte?" } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index c18a9fa2044..b145c4d1950 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Zabezpečte svůj účet vyžadováním dodatečného kroku při přihlašování." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Povolí dvoufázové přihlášení pro Vaši organizaci." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Vynutí dvoufázové přihlášení členů k Bitwardenu použitím ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Další" }, - "usFlag": { - "message": "Americká vlajka" - }, - "euFlag": { - "message": "Evropská vlajka" - }, "selectedRegionFlag": { "message": "Vlajka zvoleného regionu" }, diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 3ed144bcaf5..e2e0db2e83e 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Beskyt kontoen ved at kræve et ekstra trin under indlogning." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Aktivér totrins-login for organisationen." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Håndhæv Bitwarden totrins-login indstillinger for medlemmer vha. ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Næste" }, - "usFlag": { - "message": "Amerikansk flag" - }, - "euFlag": { - "message": "EU-flag" - }, "selectedRegionFlag": { "message": "Valgte områdeflag" }, diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 07f5f1d5e7a..5be4cb32039 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Sichern Sie Ihr Konto mit Zwei-Faktor-Authentifizierung." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Zwei-Faktor-Authentifizierung für deine Organisation aktivieren." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Erzwinge eine Zwei-Faktor-Authentifizierung in Bitwarden für Mitglieder durch Verwendung der ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -1925,7 +1928,7 @@ "message": "1 GB verschlüsselter Speicherplatz für Dateianhänge." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Proprietäre Optionen für die Zwei-Faktor Authentifizierung wie YubiKey und Duo." }, "premiumSignUpEmergency": { "message": "Notfallzugriff" @@ -7016,12 +7019,6 @@ "next": { "message": "Weiter" }, - "usFlag": { - "message": "US-Flagge" - }, - "euFlag": { - "message": "EU-Flagge" - }, "selectedRegionFlag": { "message": "Flagge der ausgewählten Region" }, diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 5103325a343..d2d87fb7180 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Ασφαλίστε το λογαριασμό σας απαιτώντας ένα επιπλέον βήμα κατά τη σύνδεση." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index f342a7e81f3..65abe5b1f72 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 5f31a22c729..dc120f301e9 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 8034131dcc5..e259d0f0732 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Sekurigu vian konton postulante plian paŝon kiam vi ensalutas." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 50e743cb655..8f58624a6f7 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Protege tu cuenta requiriendo un paso adicional a la hora de acceder." }, - "twoStepLoginOrganizationDescStart": { - "message": "Forzar opciones de inicio de sesión en dos pasos de Bitwarden para los miembros mediante el uso del ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 GB de almacenamiento de archivos cifrados." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opciones de inicio de sesión con autenticación de dos pasos propietarios como YubiKey y Duo." }, "premiumSignUpEmergency": { "message": "Acceso de emergencia" @@ -7016,12 +7019,6 @@ "next": { "message": "Siguiente" }, - "usFlag": { - "message": "Bandera de EE.UU." - }, - "euFlag": { - "message": "Bandera de EU" - }, "selectedRegionFlag": { "message": "Región seleccionada" }, diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index f31e0832366..92ac930999f 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Kaitse oma kontot, nõudes sisselogimisel lisakinnitust." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 793f4612ca2..55074ebd56f 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Ziurtatu zure kontua saioa hastean beste urrats bat egitea eskatuz." }, - "twoStepLoginOrganizationDescStart": { - "message": "Behartu kideei Bitwardenen saioa hasteko bi urratseko aukera, hau erabiliz", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index fe48e771fa0..80c93b7e597 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "با درخواست یک مرحله اضافی هنگام ورود، حساب خود را ایمن کنید." }, - "twoStepLoginOrganizationDescStart": { - "message": "گزینه‌های ورود دو مرحله ای Bitwarden را برای اعضا اعمال کنید با استفاده از ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "بعدی" }, - "usFlag": { - "message": "پرچم ایالات متحده" - }, - "euFlag": { - "message": "پرچم اتحادیه اروپا" - }, "selectedRegionFlag": { "message": "پرچم منطقه انتخاب شد" }, diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 1606ca35ff6..296d49f7133 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -1306,7 +1306,7 @@ "message": "Valitse kokoelma" }, "importTargetHint": { - "message": "Valitse tämä, jos haluat tuoda tiedoston sisällön kohteesee $DESTINATION$.", + "message": "Valitse tämä, jos haluat tuoda tiedoston sisällön kohteeseen $DESTINATION$.", "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", "placeholders": { "destination": { @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Suojaa tilisi vaatimalla sisäänkirjautumiseen toinen todennusvaihe." }, - "twoStepLoginOrganizationDescStart": { - "message": "Pakota jäsenille Bitwardenin kaksivaiheisen kirjautumisen valinnat käytännöllä: ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Omisteiset kaksivaiheisen kirjautumisen vaihtoehdot, kuten YubiKey ja Duo." }, "premiumSignUpEmergency": { "message": "Varmuuskäyttö" @@ -2041,7 +2044,7 @@ } }, "paymentChargedWithUnpaidSubscription": { - "message": "Maksutavaltasi veloitetaan kaikki maksamattomat tilauksesta." + "message": "Maksutavaltasi veloitetaan kaikki maksamattomat tilaukset." }, "paymentChargedWithTrial": { "message": "Tilauksesi sisältää ilmaisen 7 päivän kokeilujakson. Maksutapaasi ei veloiteta ennen kokeilujakson päättymistä. Voit irtisanoa tilauksen koska tahansa." @@ -3631,7 +3634,7 @@ "message": "Kohteella on vanhoja tiedostoliitteitä, jotka on korjattava." }, "attachmentFixDescription": { - "message": "Liite käyttää vanhentunutta salausta. Lataa liite, salaa se uudelleen ja lisää se uudestaan valitsemalla \"Korjaa\"." + "message": "Liite on salattu vanhentuneella salauksella. Lataa, salaa uudelleen ja lisää se uudestaan valitsemalla \"Korjaa\"." }, "fix": { "message": "Korjaa", @@ -6851,7 +6854,7 @@ "message": "Muista tämä laite" }, "uncheckIfPublicDevice": { - "message": "Poista käytöstä julkisilla laitteilla" + "message": "Poista valinta julkisilla laitteilla" }, "approveFromYourOtherDevice": { "message": "Hyväksy muilta laitteiltasi" @@ -6869,27 +6872,27 @@ "message": "Luotetut laitteet" }, "memberDecryptionOptionTdeDescriptionPartOne": { - "message": "Kun jäsenet on todennettu, he voivat purkaa holvin salauksen heidän laitteellaan säilytettävällä avaimella.", + "message": "Kun jäsenet on todennettu, he voivat purkaa holvin salauksen omalla laitteellaan säilytettävällä avaimella.", "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.'" }, "memberDecryptionOptionTdeDescriptionLinkOne": { - "message": "Yksittäinen organisaatio", + "message": "\"Yksittäinen organisaatio\"", "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.'" }, "memberDecryptionOptionTdeDescriptionPartTwo": { - "message": ",", - "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": "Kertakirjautuminen vaaditaan", - "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": { "message": "ja", "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": "\"Kertakirjautuminen vaaditaan\"", + "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": { + "message": "-käytännöt sekä", + "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.'" + }, "memberDecryptionOptionTdeDescriptionLinkThree": { - "message": "Tilin palautusavun hallinta", + "message": "\"Tilien palautusavun hallinta\"", "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.'" }, "memberDecryptionOptionTdeDescriptionPartFour": { @@ -7016,12 +7019,6 @@ "next": { "message": "Seuraava" }, - "usFlag": { - "message": "Yhdysvaltain lippu" - }, - "euFlag": { - "message": "EU:n lippu" - }, "selectedRegionFlag": { "message": "Valitun alueen lippu" }, @@ -7032,7 +7029,7 @@ "message": "Hyväksyntää pyydetty ylläpidolta" }, "adminApprovalRequestSentToAdmins": { - "message": "Pyyntösi on välitetty ylläpidolle." + "message": "Pyyntösi on välitetty ylläpidollesi." }, "youWillBeNotifiedOnceApproved": { "message": "Saat ilmoituksen kun se on hyväksytty." @@ -7041,7 +7038,7 @@ "message": "Ongelmia kirjautumisessa?" }, "loginApproved": { - "message": "Kirjautuminen hyväksyttiin" + "message": "Kirjautuminen hyväksytty" }, "userEmailMissing": { "message": "Käyttäjän sähköpostiosoite puuttuu" @@ -7061,7 +7058,7 @@ "message": "Kutsu käyttäjiä" }, "secretsManagerForPlan": { - "message": "Salaisuushallinta $PLAN$-tilaukseen", + "message": "Salaisuushallinta $PLAN$ -tilaukseen", "placeholders": { "plan": { "content": "$1", @@ -7070,7 +7067,7 @@ } }, "secretsManagerForPlanDesc": { - "message": "Kehitys ja DevOps-tiimeille salaisuuksien hallintaan ohjelmistokehityksen koko elinkaaren ajaksi." + "message": "Kehitys- ja DevOps-tiimeille ohjelmistokehityksen koko elinkaaren kestävään salaisuuksien hallintaan." }, "free2PersonOrganization": { "message": "Ilmainen kahden hengen organisaatioille" diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 1b11567b782..d395fed70d8 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Panatilihing ligtas ang account mo sa pamamagitan ng pangangailangan ng ikalawang hakbang kapag nagla-log in." }, - "twoStepLoginOrganizationDescStart": { - "message": "Ipatupad ang mga opsyon sa Dalawang-hakbang na Pag-log in sa Bitwarden para sa mga miyembro gamit ang ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 6bc5b0a1454..d71064bd50f 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Sécurisez votre compte en exigeant une étape supplémentaire lors de la connexion." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Activez l'authentification à deux facteurs pour votre organisation." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Imposer les options d'authentification à deux facteurs de Bitwarden pour les membres en utilisant le ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Suivant" }, - "usFlag": { - "message": "Drapeau des États-Unis" - }, - "euFlag": { - "message": "Drapeau de l'Union Européenne" - }, "selectedRegionFlag": { "message": "Drapeau de la région sélectionnée" }, diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 716f2d46071..ee3e68708ec 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "שפר את אבטחת החשבון שלך על ידי דרישת צעד נוסף עבור כל נסיון חיבור." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index cb737f84f44..c10d8c37284 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 3d8d176514c..83621989bf8 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Osiguraj svoj račun dodavanjem dodatnog koraka prilikom prijave." }, - "twoStepLoginOrganizationDescStart": { - "message": "Prisili korištenje prijave dvostrukom autentifikacijom koristeći", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index c8c8439626f..bec2a62727b 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "A fiók biztosítása kiegészítő lépéssel bejelentkezéskor." }, - "twoStepLoginOrganizationDescStart": { - "message": "Ezt egy nagyobb mondat részeként használjuk fel,és hivatkozásokat tartalmaznak. A teljes mondat így fog szólni: \"Kétlépcsős bejelentkezési opciók érvényesítése a tagok számára a kétlépcsős bejelentkezési szabályzat használatával.\".", + "twoStepLoginTeamsDesc": { + "message": "A kétlépcsős bejelentkezés engedélyezése a szervezethez." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "A Bitwarden kétlépcsős bejelentkezési opcióinak kényszerítése a tagoknál:", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Következő" }, - "usFlag": { - "message": "USA zászló" - }, - "euFlag": { - "message": "EU zászló" - }, "selectedRegionFlag": { "message": "Kiválasztott régió zászló" }, diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index a88799f1adc..d3854f8885d 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Amankan akun Anda dengan meminta langkah tambahan saat masuk." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index c082b3dd9ca..35f6b4d6a3c 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Proteggi il tuo account richiedendo un passaggio aggiuntivo all'accesso." }, - "twoStepLoginOrganizationDescStart": { - "message": "Forza le opzioni di verifica in due passaggi di Bitwarden per i membri usando la ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 GB di spazio di archiviazione criptato per gli allegati." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opzioni di verifica in due passaggi proprietarie come YubiKey e Duo." }, "premiumSignUpEmergency": { "message": "Accesso di emergenza" @@ -7016,12 +7019,6 @@ "next": { "message": "Avanti" }, - "usFlag": { - "message": "Bandiera degli Stati Uniti" - }, - "euFlag": { - "message": "Bandiera dell'Europa" - }, "selectedRegionFlag": { "message": "Bandiera della regione selezionata" }, diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 95647694572..45a9b4db63b 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "ログイン時に追加の手順を必要としアカウントを保護します。" }, - "twoStepLoginOrganizationDescStart": { - "message": "Bitwarden 2段階ログインの使用をメンバーに強制します", + "twoStepLoginTeamsDesc": { + "message": "組織の2段階認証を有効にする。" + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Bitwardenの2段階認証をメンバーに強制するには、次のようにします。 ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "次へ" }, - "usFlag": { - "message": "アメリカ" - }, - "euFlag": { - "message": "EU" - }, "selectedRegionFlag": { "message": "リージョン選択" }, diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 5f461f4b70b..4f2b7cc968b 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 94145e99c35..910883c83b0 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "ಲಾಗಿನ್ ಆಗುವಾಗ ಹೆಚ್ಚುವರಿ ಹಂತದ ಅಗತ್ಯವಿರುವ ಮೂಲಕ ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಸುರಕ್ಷಿತಗೊಳಿಸಿ." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 9900cfa742e..6d0cbc4bc45 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "로그인할 때 추가 단계를 요구하여 계정을 보호하십시오." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index a1f9a5bc382..3dce02f1ec4 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -859,7 +859,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Verificējiet ar savas organizācijas Duo Security, izmantojot Duo Mobile lietotni, SMS, tālruņa zvanu vai U2F drošības atslēgu.", + "message": "Apliecināšana ar savas apvienības Duo Security, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "u2fDesc": { @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Nodrošināt kontu, pieprasot papildu darbību pieteikšanās brīdī." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Iespējo divpakāpju pieteikšanaos savai apvienībai." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Piemērot Bitwarden divpakāpju pieteikšanās iespējas dalībniekiem ar ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Turpināt" }, - "usFlag": { - "message": "ASV karogs" - }, - "euFlag": { - "message": "ES karogs" - }, "selectedRegionFlag": { "message": "Atlasītā apgabala karogs" }, diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 1623cf8c2f4..c5265c0ee03 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 5d0b7f5eafd..a41bd2cd34c 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Sikre kontoen din ved å kreve et ekstra trinn når du logger på." }, - "twoStepLoginOrganizationDescStart": { - "message": "Håndhev Bitwarden tofaktor-innloggingsalternativer for medlemmer ved å bruke ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 4a8c2e06c0c..9813040b78e 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index dcead7cd8fd..91387dde607 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Beveilig je account door een extra stap te vereisen bij het inloggen." }, - "twoStepLoginOrganizationDescStart": { - "message": "Bitwarden tweestapsaanmeldingsopties afdwingen voor leden door gebruik te maken van de ", + "twoStepLoginTeamsDesc": { + "message": "Tweestapsaanmelding voor je organisatie activeren." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Bitwarden-tweestapsaanmeldingsinstellingen afdwingen voor leden via het ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Volgende" }, - "usFlag": { - "message": "VS-vlag" - }, - "euFlag": { - "message": "EU-vlag" - }, "selectedRegionFlag": { "message": "Geselecteerde regionale vlag" }, diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 22e4a7d30e1..845c218f195 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 80e7dd695d9..7b543a5f375 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Zabezpiecz swoje konto poprzez wymóg wykonania dodatkowego kroku podczas logowania." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Włącz dwustopniowe logowanie dla swojej organizacji." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Wymuszaj opcje logowania dwustopniowego Bitwarden dla użytkowników za pomocą ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Dalej" }, - "usFlag": { - "message": "Flaga USA" - }, - "euFlag": { - "message": "Flaga UE" - }, "selectedRegionFlag": { "message": "Flaga wybranego regionu" }, diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 3d931f81d07..107b604585b 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Proteja a sua conta exigindo uma etapa adicional ao iniciar sessão." }, - "twoStepLoginOrganizationDescStart": { - "message": "Requerer Login em Duas Etapas no Bitwarden para membros usando o ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 GB de armazenamento de arquivos encriptados." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opções adicionais de login em duas etapas, como YubiKey, FIDO U2F e Duo." }, "premiumSignUpEmergency": { "message": "Acesso de Emergência" @@ -7016,12 +7019,6 @@ "next": { "message": "Avançar" }, - "usFlag": { - "message": "Bandeira dos EUA" - }, - "euFlag": { - "message": "Bandeira Europa" - }, "selectedRegionFlag": { "message": "Sinalização de região selecionada" }, diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 0bf7e684824..133373fc64e 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Proteja a sua conta exigindo um passo adicional ao iniciar sessão." }, - "twoStepLoginOrganizationDescStart": { - "message": "Reforce as opções de verificação de dois passos do Bitwarden para os membros, utilizando as ", + "twoStepLoginTeamsDesc": { + "message": "Ativar verificação de dois passos para a sua organização." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Aplicar as opções de verificação de dois passos do Bitwarden para os membros, utilizando as ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opções proprietárias de verificação de dois passos, como YubiKey e Duo." }, "premiumSignUpEmergency": { "message": "Acesso de emergência" @@ -3631,7 +3634,7 @@ "message": "Este item tem anexos de ficheiros antigos que precisam de ser corrigidos." }, "attachmentFixDescription": { - "message": "Este anexo utiliza uma encriptação desatualizada. Selecione \"Corrigir\" para transferir, voltar a encriptar e voltar a carregar o anexo." + "message": "Este anexo utiliza uma encriptação desatualizada. Selecione \"Corrigir\" para transferir, reencriptar e recarregar o anexo." }, "fix": { "message": "Corrigir", @@ -7016,12 +7019,6 @@ "next": { "message": "Avançar" }, - "usFlag": { - "message": "Bandeira dos EUA" - }, - "euFlag": { - "message": "Bandeira da UE" - }, "selectedRegionFlag": { "message": "Bandeira da região selecionada" }, @@ -7157,7 +7154,7 @@ "message": "Limite de contas de serviço (opcional)" }, "maxServiceAccountCost": { - "message": "Custo máximo potencial da conta de serviço" + "message": "Custo potencial máximo da conta de serviço" }, "loggedInExclamation": { "message": "Sessão iniciada!" diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 27e9d4c17b7..73f07464a8b 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Vă securizează contul solicitând un pas suplimentar la conectare." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index af9a088993f..f63c97f69f6 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -982,7 +982,7 @@ "message": "Экспорт" }, "exportVault": { - "message": "Экспортировать хранилище" + "message": "Экспорт хранилища" }, "exportSecrets": { "message": "Экспорт секретов" @@ -1319,10 +1319,10 @@ "message": "Файл содержит неназначенные элементы." }, "selectFormat": { - "message": "Выберите формат файла импорта" + "message": "Выберите формат импортируемого файла" }, "selectImportFile": { - "message": "Выберите файл импорта" + "message": "Выберите импортируемый файл" }, "chooseFile": { "message": "Выбрать файл" @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Защитите свою учетную запись, при помощи дополнительного шага при авторизации." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Включить двухэтапную аутентификацию для вашей организации." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Применить параметры двухэтапной аутентификации Bitwarden для пользователей, используя ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -3649,7 +3652,7 @@ "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "fingerprintMatchInfo": { - "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка пальца совпадает на другом устройстве." + "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка совпадает на другом устройстве." }, "fingerprintPhraseHeader": { "message": "Фраза отпечатка" @@ -6775,7 +6778,7 @@ "message": "Удалить доступ" }, "checkForBreaches": { - "message": "Проверьте известные случаи утечки данных для этого пароля" + "message": "Проверять известные случаи утечки данных для этого пароля" }, "exposedMasterPassword": { "message": "Мастер-пароль скомпрометирован" @@ -7016,12 +7019,6 @@ "next": { "message": "Далее" }, - "usFlag": { - "message": "Флаг США" - }, - "euFlag": { - "message": "Флаг ЕС" - }, "selectedRegionFlag": { "message": "Флаг выбранного региона" }, diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index adff63eb2fc..cf61fe2da62 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index fd1d6d707bf..f666bd0aa1f 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Zabezpečte svoj účet požadovaním ďalšieho kroku pri prihlasovaní." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Povoľte dvojstupňové prihlásenie pre vašu organizáciu." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Vynútiť Bitwarden dvojstupňové prihlásenie pre členov použitím ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -1925,7 +1928,7 @@ "message": "1 GB šifrovaného úložiska pre prílohy." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Proprietárne možnosti dvojstupňového prihlásenia ako napríklad YubiKey a Duo." }, "premiumSignUpEmergency": { "message": "Núdzový prístup" @@ -7016,12 +7019,6 @@ "next": { "message": "Ďalej" }, - "usFlag": { - "message": "Vlajka USA" - }, - "euFlag": { - "message": "Vlajka EU" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index c3f3e479f1f..a0f735bc2d3 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Zavarujte svoj račun z dodatno zaščito pri prijavi." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index f1ae3e829c1..926c1650e7c 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Заштитите свој налог захтевањем додатног корака приликом пријављивања." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Омогућите пријаву у два корака за вашу организацију." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Примени Bitwarden опције за пријаву у два корака за чланове користећи ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -1925,7 +1928,7 @@ "message": "1ГБ шифровано складиште за прилоге." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Приоритарне опције пријаве у два корака као што су YubiKey и Duo." }, "premiumSignUpEmergency": { "message": "Улаз у хитним случајевима" @@ -2041,7 +2044,7 @@ } }, "paymentChargedWithUnpaidSubscription": { - "message": "Your payment method will be charged for any unpaid subscriptions." + "message": "Ваш начин плаћања ће бити наплаћен за све неплаћене претплате." }, "paymentChargedWithTrial": { "message": "Ваш план долази са бесплатним 7-дневним пробним периодом. Начин плаћања неће бити наплаћен док се пробно време не заврши. Наплата ће се вршити периодично, сваки $INTERVAL$. Можете отказати било када." @@ -7016,12 +7019,6 @@ "next": { "message": "Следеће" }, - "usFlag": { - "message": "Америчка застава" - }, - "euFlag": { - "message": "ЕУ застава" - }, "selectedRegionFlag": { "message": "Одабрана застава" }, diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index a50306355db..b0e7500e183 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 7d2e12239f7..5ca6ab8f146 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Säkra ditt konto genom att kräva ett ytterligare steg vid inloggning." }, - "twoStepLoginOrganizationDescStart": { - "message": "Kräv att medlemmar använder tvåstegsverifiering genom att använda ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Nästa" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index a71551e7710..4ed205c1f73 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 25206db2e37..7948cbeee12 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Secure your account by requiring an additional step when logging in." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 55b2c51548e..d6b97c70418 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Oturum açarken ek bir adım talep ederek hesabınızı güvenceye alabilirsiniz." }, - "twoStepLoginOrganizationDescStart": { - "message": "Üyeler için Bitwarden iki aşamalı giriş seçeneklerini ", + "twoStepLoginTeamsDesc": { + "message": "Kuruluşunuz için iki adımlı oturum açmayı etkinleştirin." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Bitwarden'ı kullanarak üyeler için İki Adımlı Giriş seçeneklerini zorunlu kılın ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "İleri" }, - "usFlag": { - "message": "ABD bayrağı" - }, - "euFlag": { - "message": "AB bayrağı" - }, "selectedRegionFlag": { "message": "Seçilen bölgenin bayrağı" }, diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 4c336c0b30d..f341ba6b6a8 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -392,7 +392,7 @@ "message": "Редагування" }, "viewItem": { - "message": "Перегляд запису" + "message": "Переглянути запис" }, "new": { "message": "Новий", @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "Захистіть обліковий запис, вимагаючи додатковий крок перевірки під час входу." }, - "twoStepLoginOrganizationDescStart": { - "message": "Зобов'язувати учасників використовувати двоетапну перевірку Bitwarden за допомогою ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 ГБ зашифрованого сховища для файлів." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Додаткові можливості двоетапної авторизації, як-от YubiKey та Duo." }, "premiumSignUpEmergency": { "message": "Екстрений доступ" @@ -2041,7 +2044,7 @@ } }, "paymentChargedWithUnpaidSubscription": { - "message": "Your payment method will be charged for any unpaid subscriptions." + "message": "Ваш спосіб оплати буди використано для платежів за будь-які несплачені передплати." }, "paymentChargedWithTrial": { "message": "Ваш тарифний план має 7 днів безплатного пробного періоду. З вас не буде стягнуто плату до завершення цього періоду. Ви можете скасувати це в будь-який час." @@ -3084,7 +3087,7 @@ "message": "Пристрій" }, "view": { - "message": "Перегляд" + "message": "Переглянути" }, "invalidDateRange": { "message": "Недійсний проміжок часу." @@ -7016,12 +7019,6 @@ "next": { "message": "Далі" }, - "usFlag": { - "message": "Прапор США" - }, - "euFlag": { - "message": "Прапор ЄС" - }, "selectedRegionFlag": { "message": "Прапор вибраного регіону" }, diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index fb25525d61a..f7aa1449d12 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -1425,7 +1425,10 @@ "twoStepLoginDesc": { "message": "Bảo mật tài khoản bằng các phương pháp sau khi đăng nhập." }, - "twoStepLoginOrganizationDescStart": { + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index be07ab7e208..7a4ecc7db08 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -609,7 +609,7 @@ "message": "使用设备登录" }, "loginWithDeviceEnabledNote": { - "message": "设备登录必须在 Bitwarden 应用程序的设置中设启用。需要其他选项吗?" + "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他登录选项吗?" }, "loginWithMasterPassword": { "message": "使用主密码登录" @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "在登录时要求使用额外的步骤来保护您的账户。" }, - "twoStepLoginOrganizationDescStart": { - "message": "要为成员实施 Bitwarden 两步登录选项,请使用 ", + "twoStepLoginTeamsDesc": { + "message": "为您的组织启用两步登录。" + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1928,7 @@ "message": "1 GB 文件附件加密存储。" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "专有的两步登录选项,如 YubiKey 和 Duo。" }, "premiumSignUpEmergency": { "message": "紧急访问" @@ -3649,7 +3652,7 @@ "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "fingerprintMatchInfo": { - "message": "请确保您的密码库已解锁,并且指纹短语与其他设备匹配。" + "message": "请确保您的密码库已解锁,并且指纹短语与其他设备上的相匹配。" }, "fingerprintPhraseHeader": { "message": "指纹短语" @@ -7016,12 +7019,6 @@ "next": { "message": "下一步" }, - "usFlag": { - "message": "美国旗帜" - }, - "euFlag": { - "message": "欧盟旗帜" - }, "selectedRegionFlag": { "message": "选择的区域旗帜" }, diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index acdd1aa1a79..f7692a0a3e4 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -1425,8 +1425,11 @@ "twoStepLoginDesc": { "message": "在登入時執行額外的步驟來保護您的帳戶。" }, - "twoStepLoginOrganizationDescStart": { - "message": "讓成員使用 Bitwarden 兩步驟登入,藉由 ", + "twoStepLoginTeamsDesc": { + "message": "Enable two-step login for your organization." + }, + "twoStepLoginEnterpriseDescStart": { + "message": "Enforce Bitwarden Two-step Login options for members by using the ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7016,12 +7019,6 @@ "next": { "message": "Next" }, - "usFlag": { - "message": "US flag" - }, - "euFlag": { - "message": "EU flag" - }, "selectedRegionFlag": { "message": "Selected region flag" }, From 61e1bc1a1c1c36f29426003f8425337dcf8191a4 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Sat, 9 Sep 2023 00:05:37 +1000 Subject: [PATCH 114/135] [AC-1479][BEEEP] Refactor ConfigService to improve observable usage (#5602) * refactor ConfigService to use observables * make environmentService.urls a ReplaySubject --------- Co-authored-by: Hinton --- .../browser/src/background/main.background.ts | 13 +- .../src/background/runtime.background.ts | 4 +- .../services/browser-config.service.ts | 18 +- .../src/popup/services/init.service.ts | 6 +- .../src/popup/services/services.module.ts | 4 +- apps/desktop/src/app/app.component.ts | 2 +- apps/desktop/src/app/services/init.service.ts | 6 +- apps/web/src/app/app.component.ts | 2 +- ...ganization-subscription-cloud.component.ts | 2 +- .../billing/settings/add-credit.component.ts | 3 +- .../settings/organization-plans.component.ts | 2 +- .../environment-selector.component.ts | 2 +- apps/web/src/app/core/init.service.ts | 6 +- .../bit-web/src/app/auth/sso/sso.component.ts | 2 +- .../environment-selector.component.ts | 2 +- .../src/auth/components/sso.component.spec.ts | 2 +- .../src/auth/components/sso.component.ts | 2 +- .../components/two-factor.component.spec.ts | 2 +- .../auth/components/two-factor.component.ts | 2 +- .../directives/if-feature.directive.spec.ts | 23 +-- .../src/directives/if-feature.directive.ts | 17 +- .../src/guard/feature-flag.guard.spec.ts | 8 +- libs/angular/src/guard/feature-flag.guard.ts | 10 +- .../src/services/jslib-services.module.ts | 6 +- libs/common/src/enums/feature-flag.enum.ts | 3 + .../config/config.service.abstraction.ts | 22 ++- .../services/config/config.service.spec.ts | 174 ++++++++++++++++++ .../services/config/config.service.ts | 165 +++++++++-------- .../platform/services/environment.service.ts | 4 +- 29 files changed, 356 insertions(+), 158 deletions(-) create mode 100644 libs/common/src/platform/services/config/config.service.spec.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index f9963bcf7da..e646b98d414 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -35,7 +35,6 @@ import { UserVerificationApiService } from "@bitwarden/common/auth/services/user import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -53,7 +52,6 @@ import { StateFactory } from "@bitwarden/common/platform/factories/state-factory import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; -import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; @@ -123,6 +121,7 @@ import { flagEnabled } from "../platform/flags"; import { UpdateBadge } from "../platform/listeners/update-badge"; import BrowserPopoutWindowService from "../platform/popup/browser-popout-window.service"; import { BrowserStateService as StateServiceAbstraction } from "../platform/services/abstractions/browser-state.service"; +import { BrowserConfigService } from "../platform/services/browser-config.service"; import { BrowserCryptoService } from "../platform/services/browser-crypto.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import { BrowserI18nService } from "../platform/services/browser-i18n.service"; @@ -200,7 +199,7 @@ export default class MainBackground { avatarUpdateService: AvatarUpdateServiceAbstraction; mainContextMenuHandler: MainContextMenuHandler; cipherContextMenuHandler: CipherContextMenuHandler; - configService: ConfigServiceAbstraction; + configService: BrowserConfigService; configApiService: ConfigApiServiceAbstraction; devicesApiService: DevicesApiServiceAbstraction; devicesService: DevicesServiceAbstraction; @@ -533,12 +532,15 @@ export default class MainBackground { this.authService, this.messagingService ); + this.configApiService = new ConfigApiService(this.apiService, this.authService); - this.configService = new ConfigService( + + this.configService = new BrowserConfigService( this.stateService, this.configApiService, this.authService, - this.environmentService + this.environmentService, + true ); this.browserPopoutWindowService = new BrowserPopoutWindowService(); @@ -682,6 +684,7 @@ export default class MainBackground { await this.notificationBackground.init(); await this.commandsBackground.init(); + this.configService.init(); this.twoFactorService.init(); await this.tabsBackground.init(); diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index c1cfdf0420f..dcf828ef4a0 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -103,7 +103,7 @@ export default class RuntimeBackground { await this.main.refreshMenu(); }, 2000); this.main.avatarUpdateService.loadColorFromState(); - this.configService.fetchServerConfig(); + this.configService.triggerServerConfigFetch(); } break; case "openPopup": @@ -139,7 +139,7 @@ export default class RuntimeBackground { case "triggerAutofillScriptInjection": await this.autofillService.injectAutofillScripts( sender, - await this.configService.getFeatureFlagBool(FeatureFlag.AutofillV2) + await this.configService.getFeatureFlag(FeatureFlag.AutofillV2) ); break; case "bgCollectPageDetails": diff --git a/apps/browser/src/platform/services/browser-config.service.ts b/apps/browser/src/platform/services/browser-config.service.ts index 68237b4c206..f928fdd0726 100644 --- a/apps/browser/src/platform/services/browser-config.service.ts +++ b/apps/browser/src/platform/services/browser-config.service.ts @@ -1,6 +1,10 @@ -import { BehaviorSubject } from "rxjs"; +import { ReplaySubject } from "rxjs"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { browserSession, sessionSync } from "../decorators/session-sync-observable"; @@ -8,5 +12,15 @@ import { browserSession, sessionSync } from "../decorators/session-sync-observab @browserSession export class BrowserConfigService extends ConfigService { @sessionSync({ initializer: ServerConfig.fromJSON }) - protected _serverConfig: BehaviorSubject; + protected _serverConfig: ReplaySubject; + + constructor( + stateService: StateService, + configApiService: ConfigApiServiceAbstraction, + authService: AuthService, + environmentService: EnvironmentService, + subscribe = false + ) { + super(stateService, configApiService, authService, environmentService, subscribe); + } } diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 23ae6e8e892..c9a1ffb720e 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -4,6 +4,7 @@ import { AbstractThemingService } from "@bitwarden/angular/services/theming/them import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { BrowserStateService as StateServiceAbstraction } from "../../platform/services/abstractions/browser-state.service"; @@ -17,7 +18,8 @@ export class InitService { private popupUtilsService: PopupUtilsService, private stateService: StateServiceAbstraction, private logService: LogServiceAbstraction, - private themingService: AbstractThemingService + private themingService: AbstractThemingService, + private configService: ConfigService ) {} init() { @@ -50,6 +52,8 @@ export class InitService { htmlEl.classList.add("force_redraw"); this.logService.info("Force redraw is on"); } + + this.configService.init(); }; } } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 261f6abe37d..4e4f914f230 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -36,7 +36,6 @@ import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { LoginService } from "@bitwarden/common/auth/services/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; -import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -57,6 +56,7 @@ import { } from "@bitwarden/common/platform/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { SearchService } from "@bitwarden/common/services/search.service"; @@ -495,7 +495,7 @@ function getBgService(service: keyof MainBackground) { deps: [StateServiceAbstraction, PlatformUtilsService], }, { - provide: ConfigServiceAbstraction, + provide: ConfigService, useClass: BrowserConfigService, deps: [ StateServiceAbstraction, diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index b5321a2bc83..39e2d2f8b11 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -232,7 +232,7 @@ export class AppComponent implements OnInit, OnDestroy { break; case "syncCompleted": await this.updateAppMenu(); - await this.configService.fetchServerConfig(); + this.configService.triggerServerConfigFetch(); break; case "openSettings": await this.openModal(SettingsComponent, this.settingsRef); diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 34300aed931..0d60a1140f8 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -11,6 +11,7 @@ import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service"; @@ -35,7 +36,8 @@ export class InitService { private cryptoService: CryptoServiceAbstraction, private nativeMessagingService: NativeMessagingService, private themingService: AbstractThemingService, - private encryptService: EncryptService + private encryptService: EncryptService, + private configService: ConfigService ) {} init() { @@ -71,6 +73,8 @@ export class InitService { const containerService = new ContainerService(this.cryptoService, this.encryptService); containerService.attachToGlobal(this.win); + + this.configService.init(); }; } } diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 3066dbe093c..5b265278423 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -138,7 +138,7 @@ export class AppComponent implements OnDestroy, OnInit { case "syncStarted": break; case "syncCompleted": - await this.configService.fetchServerConfig(); + this.configService.triggerServerConfigFetch(); break; case "upgradeOrganization": { const upgradeConfirmed = await this.dialogService.openSimpleDialog({ 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 dedc99edcb1..b419f743262 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 @@ -135,7 +135,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy this.loading = false; // Remove the remaining lines when the sm-ga-billing flag is deleted - const smBillingEnabled = await this.configService.getFeatureFlagBool( + const smBillingEnabled = await this.configService.getFeatureFlag( FeatureFlag.SecretsManagerBilling ); this.showSecretsManagerSubscribe = this.showSecretsManagerSubscribe && smBillingEnabled; diff --git a/apps/web/src/app/billing/settings/add-credit.component.ts b/apps/web/src/app/billing/settings/add-credit.component.ts index c59886b4b40..2a60e65f2ac 100644 --- a/apps/web/src/app/billing/settings/add-credit.component.ts +++ b/apps/web/src/app/billing/settings/add-credit.component.ts @@ -7,6 +7,7 @@ import { Output, ViewChild, } from "@angular/core"; +import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -79,7 +80,7 @@ export class AddCreditComponent implements OnInit { this.email = this.subject; this.ppButtonCustomField = "user_id:" + this.userId; } - this.region = await this.configService.getCloudRegion(); + this.region = await firstValueFrom(this.configService.cloudRegion$); this.ppButtonCustomField += ",account_credit:1"; this.ppButtonCustomField += `,region:${this.region}`; this.returnUrl = window.location.href; diff --git a/apps/web/src/app/billing/settings/organization-plans.component.ts b/apps/web/src/app/billing/settings/organization-plans.component.ts index c84d9511a17..6b0bd65d196 100644 --- a/apps/web/src/app/billing/settings/organization-plans.component.ts +++ b/apps/web/src/app/billing/settings/organization-plans.component.ts @@ -169,7 +169,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.singleOrgPolicyAppliesToActiveUser = policyAppliesToActiveUser; }); - this.showSecretsManagerSubscribe = await this.configService.getFeatureFlagBool( + this.showSecretsManagerSubscribe = await this.configService.getFeatureFlag( FeatureFlag.SecretsManagerBilling, false ); diff --git a/apps/web/src/app/components/environment-selector/environment-selector.component.ts b/apps/web/src/app/components/environment-selector/environment-selector.component.ts index 99146134b68..3c1c5a19732 100644 --- a/apps/web/src/app/components/environment-selector/environment-selector.component.ts +++ b/apps/web/src/app/components/environment-selector/environment-selector.component.ts @@ -25,7 +25,7 @@ export class EnvironmentSelectorComponent implements OnInit { routeAndParams: string; async ngOnInit() { - this.euServerFlagEnabled = await this.configService.getFeatureFlagBool( + this.euServerFlagEnabled = await this.configService.getFeatureFlag( FeatureFlag.DisplayEuEnvironmentFlag ); const domain = Utils.getDomain(window.location.href); diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index 3437c4f3e93..f171217d3cd 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -13,6 +13,7 @@ import { } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service"; @@ -32,7 +33,8 @@ export class InitService { private stateService: StateServiceAbstraction, private cryptoService: CryptoServiceAbstraction, private themingService: AbstractThemingService, - private encryptService: EncryptService + private encryptService: EncryptService, + private configService: ConfigService ) {} init() { @@ -57,6 +59,8 @@ export class InitService { await this.themingService.monitorThemeChanges(); const containerService = new ContainerService(this.cryptoService, this.encryptService); containerService.attachToGlobal(this.win); + + this.configService.init(); }; } } diff --git a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts index e159d4744b4..21cda4bbb05 100644 --- a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts +++ b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts @@ -235,7 +235,7 @@ export class SsoComponent implements OnInit, OnDestroy { ) .subscribe(); - const tdeFeatureFlag = await this.configService.getFeatureFlagBool( + const tdeFeatureFlag = await this.configService.getFeatureFlag( FeatureFlag.TrustedDeviceEncryption ); diff --git a/libs/angular/src/auth/components/environment-selector.component.ts b/libs/angular/src/auth/components/environment-selector.component.ts index 498d72b01ee..19dc95dbcf6 100644 --- a/libs/angular/src/auth/components/environment-selector.component.ts +++ b/libs/angular/src/auth/components/environment-selector.component.ts @@ -89,7 +89,7 @@ export class EnvironmentSelectorComponent implements OnInit, OnDestroy { async updateEnvironmentInfo() { this.selectedEnvironment = this.environmentService.selectedRegion; - this.euServerFlagEnabled = await this.configService.getFeatureFlagBool( + this.euServerFlagEnabled = await this.configService.getFeatureFlag( FeatureFlag.DisplayEuEnvironmentFlag ); } diff --git a/libs/angular/src/auth/components/sso.component.spec.ts b/libs/angular/src/auth/components/sso.component.spec.ts index 68c41b66119..894232af443 100644 --- a/libs/angular/src/auth/components/sso.component.spec.ts +++ b/libs/angular/src/auth/components/sso.component.spec.ts @@ -331,7 +331,7 @@ describe("SsoComponent", () => { describe("Trusted Device Encryption scenarios", () => { beforeEach(() => { - mockConfigService.getFeatureFlagBool.mockResolvedValue(true); // TDE enabled + mockConfigService.getFeatureFlag.mockResolvedValue(true); // TDE enabled }); describe("Given Trusted Device Encryption is enabled and user needs to set a master password", () => { diff --git a/libs/angular/src/auth/components/sso.component.ts b/libs/angular/src/auth/components/sso.component.ts index 8030e880a54..7e6aca7ec07 100644 --- a/libs/angular/src/auth/components/sso.component.ts +++ b/libs/angular/src/auth/components/sso.component.ts @@ -242,7 +242,7 @@ export class SsoComponent { private async isTrustedDeviceEncEnabled( trustedDeviceOption: TrustedDeviceUserDecryptionOption ): Promise { - const trustedDeviceEncryptionFeatureActive = await this.configService.getFeatureFlagBool( + const trustedDeviceEncryptionFeatureActive = await this.configService.getFeatureFlag( FeatureFlag.TrustedDeviceEncryption ); diff --git a/libs/angular/src/auth/components/two-factor.component.spec.ts b/libs/angular/src/auth/components/two-factor.component.spec.ts index 470c9d4eb7c..9e147f33573 100644 --- a/libs/angular/src/auth/components/two-factor.component.spec.ts +++ b/libs/angular/src/auth/components/two-factor.component.spec.ts @@ -376,7 +376,7 @@ describe("TwoFactorComponent", () => { describe("Trusted Device Encryption scenarios", () => { beforeEach(() => { - mockConfigService.getFeatureFlagBool.mockResolvedValue(true); + mockConfigService.getFeatureFlag.mockResolvedValue(true); }); describe("Given Trusted Device Encryption is enabled and user needs to set a master password", () => { diff --git a/libs/angular/src/auth/components/two-factor.component.ts b/libs/angular/src/auth/components/two-factor.component.ts index 0a06d393158..9a6352283a0 100644 --- a/libs/angular/src/auth/components/two-factor.component.ts +++ b/libs/angular/src/auth/components/two-factor.component.ts @@ -257,7 +257,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI trustedDeviceOption: TrustedDeviceUserDecryptionOption ): Promise { const ssoTo2faFlowActive = this.route.snapshot.queryParamMap.get("sso") === "true"; - const trustedDeviceEncryptionFeatureActive = await this.configService.getFeatureFlagBool( + const trustedDeviceEncryptionFeatureActive = await this.configService.getFeatureFlag( FeatureFlag.TrustedDeviceEncryption ); diff --git a/libs/angular/src/directives/if-feature.directive.spec.ts b/libs/angular/src/directives/if-feature.directive.spec.ts index bf73a172a55..9d492b7f01f 100644 --- a/libs/angular/src/directives/if-feature.directive.spec.ts +++ b/libs/angular/src/directives/if-feature.directive.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { mock, MockProxy } from "jest-mock-extended"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { FeatureFlag, FeatureFlagValue } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -41,21 +41,12 @@ describe("IfFeatureDirective", () => { let content: HTMLElement; let mockConfigService: MockProxy; - const mockConfigFlagValue = (flag: FeatureFlag, flagValue: any) => { - if (typeof flagValue === "boolean") { - mockConfigService.getFeatureFlagBool.mockImplementation((f, defaultValue = false) => - flag == f ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) - ); - } else if (typeof flagValue === "string") { - mockConfigService.getFeatureFlagString.mockImplementation((f, defaultValue = "") => - flag == f ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) - ); - } else if (typeof flagValue === "number") { - mockConfigService.getFeatureFlagNumber.mockImplementation((f, defaultValue = 0) => - flag == f ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) - ); - } + const mockConfigFlagValue = (flag: FeatureFlag, flagValue: FeatureFlagValue) => { + mockConfigService.getFeatureFlag.mockImplementation((f, defaultValue) => + flag == f ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) + ); }; + const queryContent = (testId: string) => fixture.debugElement.query(By.css(`[data-testid="${testId}"]`))?.nativeElement; @@ -126,7 +117,7 @@ describe("IfFeatureDirective", () => { }); it("hides content when the directive throws an unexpected exception", async () => { - mockConfigService.getFeatureFlagBool.mockImplementation(() => Promise.reject("Some error")); + mockConfigService.getFeatureFlag.mockImplementation(() => Promise.reject("Some error")); fixture.detectChanges(); await fixture.whenStable(); diff --git a/libs/angular/src/directives/if-feature.directive.ts b/libs/angular/src/directives/if-feature.directive.ts index 1a0ee35dc68..e9aca531bb7 100644 --- a/libs/angular/src/directives/if-feature.directive.ts +++ b/libs/angular/src/directives/if-feature.directive.ts @@ -1,12 +1,9 @@ import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from "@angular/core"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { FeatureFlag, FeatureFlagValue } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -// Replace this with a type safe lookup of the feature flag values in PM-2282 -type FlagValue = boolean | number | string; - /** * Directive that conditionally renders the element when the feature flag is enabled and/or * matches the value specified by {@link appIfFeatureValue}. @@ -26,7 +23,7 @@ export class IfFeatureDirective implements OnInit { * Optional value to compare against the value of the feature flag in the config service. * @default true */ - @Input() appIfFeatureValue: FlagValue = true; + @Input() appIfFeatureValue: FeatureFlagValue = true; private hasView = false; @@ -39,15 +36,7 @@ export class IfFeatureDirective implements OnInit { async ngOnInit() { try { - let flagValue: FlagValue; - - if (typeof this.appIfFeatureValue === "boolean") { - flagValue = await this.configService.getFeatureFlagBool(this.appIfFeature); - } else if (typeof this.appIfFeatureValue === "number") { - flagValue = await this.configService.getFeatureFlagNumber(this.appIfFeature); - } else if (typeof this.appIfFeatureValue === "string") { - flagValue = await this.configService.getFeatureFlagString(this.appIfFeature); - } + const flagValue = await this.configService.getFeatureFlag(this.appIfFeature); if (this.appIfFeatureValue === flagValue) { if (!this.hasView) { diff --git a/libs/angular/src/guard/feature-flag.guard.spec.ts b/libs/angular/src/guard/feature-flag.guard.spec.ts index bba879278ff..1ac2a90ae0d 100644 --- a/libs/angular/src/guard/feature-flag.guard.spec.ts +++ b/libs/angular/src/guard/feature-flag.guard.spec.ts @@ -30,15 +30,15 @@ describe("canAccessFeature", () => { // Mock the correct getter based on the type of flagValue; also mock default values if one is not provided if (typeof flagValue === "boolean") { - mockConfigService.getFeatureFlagBool.mockImplementation((flag, defaultValue = false) => + mockConfigService.getFeatureFlag.mockImplementation((flag, defaultValue = false) => flag == testFlag ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) ); } else if (typeof flagValue === "string") { - mockConfigService.getFeatureFlagString.mockImplementation((flag, defaultValue = "") => + mockConfigService.getFeatureFlag.mockImplementation((flag, defaultValue = "") => flag == testFlag ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) ); } else if (typeof flagValue === "number") { - mockConfigService.getFeatureFlagNumber.mockImplementation((flag, defaultValue = 0) => + mockConfigService.getFeatureFlag.mockImplementation((flag, defaultValue = 0) => flag == testFlag ? Promise.resolve(flagValue) : Promise.resolve(defaultValue) ); } @@ -143,7 +143,7 @@ describe("canAccessFeature", () => { it("fails to navigate when the config service throws an unexpected exception", async () => { const { router } = setup(canAccessFeature(testFlag), true); - mockConfigService.getFeatureFlagBool.mockImplementation(() => Promise.reject("Some error")); + mockConfigService.getFeatureFlag.mockImplementation(() => Promise.reject("Some error")); await router.navigate([featureRoute]); diff --git a/libs/angular/src/guard/feature-flag.guard.ts b/libs/angular/src/guard/feature-flag.guard.ts index f4596f3cf4b..d9297cbd978 100644 --- a/libs/angular/src/guard/feature-flag.guard.ts +++ b/libs/angular/src/guard/feature-flag.guard.ts @@ -29,16 +29,8 @@ export const canAccessFeature = ( const i18nService = inject(I18nService); const logService = inject(LogService); - let flagValue: FlagValue; - try { - if (typeof requiredFlagValue === "boolean") { - flagValue = await configService.getFeatureFlagBool(featureFlag); - } else if (typeof requiredFlagValue === "number") { - flagValue = await configService.getFeatureFlagNumber(featureFlag); - } else if (typeof requiredFlagValue === "string") { - flagValue = await configService.getFeatureFlagString(featureFlag); - } + const flagValue = await configService.getFeatureFlag(featureFlag); if (flagValue === requiredFlagValue) { return true; diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index df64c25c914..5d279657101 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -640,7 +640,7 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; useClass: SyncNotifierService, }, { - provide: ConfigServiceAbstraction, + provide: ConfigService, useClass: ConfigService, deps: [ StateServiceAbstraction, @@ -649,6 +649,10 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; EnvironmentServiceAbstraction, ], }, + { + provide: ConfigServiceAbstraction, + useExisting: ConfigService, + }, { provide: ConfigApiServiceAbstraction, useClass: ConfigApiService, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 7016849b3bc..8f30478ced5 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -5,3 +5,6 @@ export enum FeatureFlag { AutofillV2 = "autofill-v2", SecretsManagerBilling = "sm-ga-billing", } + +// Replace this with a type safe lookup of the feature flag values in PM-2282 +export type FeatureFlagValue = number | string | boolean; diff --git a/libs/common/src/platform/abstractions/config/config.service.abstraction.ts b/libs/common/src/platform/abstractions/config/config.service.abstraction.ts index 13e44b9f5eb..59f87b0fa29 100644 --- a/libs/common/src/platform/abstractions/config/config.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/config/config.service.abstraction.ts @@ -1,14 +1,26 @@ import { Observable } from "rxjs"; import { FeatureFlag } from "../../../enums/feature-flag.enum"; +import { Region } from "../environment.service"; import { ServerConfig } from "./server-config"; export abstract class ConfigServiceAbstraction { serverConfig$: Observable; - fetchServerConfig: () => Promise; - getFeatureFlagBool: (key: FeatureFlag, defaultValue?: boolean) => Promise; - getFeatureFlagString: (key: FeatureFlag, defaultValue?: string) => Promise; - getFeatureFlagNumber: (key: FeatureFlag, defaultValue?: number) => Promise; - getCloudRegion: (defaultValue?: string) => Promise; + cloudRegion$: Observable; + getFeatureFlag$: ( + key: FeatureFlag, + defaultValue?: T + ) => Observable; + getFeatureFlag: ( + key: FeatureFlag, + defaultValue?: T + ) => Promise; + + /** + * Force ConfigService to fetch an updated config from the server and emit it from serverConfig$ + * @deprecated The service implementation should subscribe to an observable and use that to trigger a new fetch from + * server instead + */ + triggerServerConfigFetch: () => void; } diff --git a/libs/common/src/platform/services/config/config.service.spec.ts b/libs/common/src/platform/services/config/config.service.spec.ts new file mode 100644 index 00000000000..511ecfd5c86 --- /dev/null +++ b/libs/common/src/platform/services/config/config.service.spec.ts @@ -0,0 +1,174 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { ReplaySubject, skip, take } from "rxjs"; + +import { AuthService } from "../../../auth/abstractions/auth.service"; +import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; +import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-api.service.abstraction"; +import { ServerConfig } from "../../abstractions/config/server-config"; +import { EnvironmentService } from "../../abstractions/environment.service"; +import { StateService } from "../../abstractions/state.service"; +import { ServerConfigData } from "../../models/data/server-config.data"; +import { + EnvironmentServerConfigResponse, + ServerConfigResponse, + ThirdPartyServerConfigResponse, +} from "../../models/response/server-config.response"; + +import { ConfigService } from "./config.service"; + +describe("ConfigService", () => { + let stateService: MockProxy; + let configApiService: MockProxy; + let authService: MockProxy; + let environmentService: MockProxy; + + let serverResponseCount: number; // increments to track distinct responses received from server + + // Observables will start emitting as soon as this is created, so only create it + // after everything is mocked + const configServiceFactory = () => { + const configService = new ConfigService( + stateService, + configApiService, + authService, + environmentService + ); + configService.init(); + return configService; + }; + + beforeEach(() => { + stateService = mock(); + configApiService = mock(); + authService = mock(); + environmentService = mock(); + environmentService.urls = new ReplaySubject(1); + + serverResponseCount = 1; + configApiService.get.mockImplementation(() => + Promise.resolve(serverConfigResponseFactory("server" + serverResponseCount++)) + ); + + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("Loads config from storage", (done) => { + const storedConfigData = serverConfigDataFactory("storedConfig"); + stateService.getServerConfig.mockResolvedValueOnce(storedConfigData); + + const configService = configServiceFactory(); + + configService.serverConfig$.pipe(take(1)).subscribe((config) => { + expect(config).toEqual(new ServerConfig(storedConfigData)); + expect(stateService.getServerConfig).toHaveBeenCalledTimes(1); + expect(stateService.setServerConfig).not.toHaveBeenCalled(); + done(); + }); + }); + + describe("Fetches config from server", () => { + beforeEach(() => { + stateService.getServerConfig.mockResolvedValueOnce(null); + }); + + it.each([1, 2, 3])( + "after %p hour/s", + (hours: number, done: jest.DoneCallback) => { + const configService = configServiceFactory(); + + // skip initial load from storage, plus previous hours (if any) + configService.serverConfig$.pipe(skip(hours), take(1)).subscribe((config) => { + try { + expect(config.gitHash).toEqual("server" + hours); + expect(configApiService.get).toHaveBeenCalledTimes(hours); + done(); + } catch (e) { + done(e); + } + }); + + const oneHourInMs = 1000 * 3600; + jest.advanceTimersByTime(oneHourInMs * hours + 1); + } + ); + + it("when environment URLs change", (done) => { + const configService = configServiceFactory(); + + // skip initial load from storage + configService.serverConfig$.pipe(skip(1), take(1)).subscribe((config) => { + try { + expect(config.gitHash).toEqual("server1"); + done(); + } catch (e) { + done(e); + } + }); + + (environmentService.urls as ReplaySubject).next(); + }); + + it("when triggerServerConfigFetch() is called", (done) => { + const configService = configServiceFactory(); + + // skip initial load from storage + configService.serverConfig$.pipe(skip(1), take(1)).subscribe((config) => { + try { + expect(config.gitHash).toEqual("server1"); + done(); + } catch (e) { + done(e); + } + }); + + configService.triggerServerConfigFetch(); + }); + }); + + it("Saves server config to storage when the user is logged in", (done) => { + stateService.getServerConfig.mockResolvedValueOnce(null); + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked); + const configService = configServiceFactory(); + + // skip initial load from storage + configService.serverConfig$.pipe(skip(1), take(1)).subscribe(() => { + try { + expect(stateService.setServerConfig).toHaveBeenCalledWith( + expect.objectContaining({ gitHash: "server1" }) + ); + done(); + } catch (e) { + done(e); + } + }); + + configService.triggerServerConfigFetch(); + }); +}); + +function serverConfigDataFactory(gitHash: string) { + return new ServerConfigData(serverConfigResponseFactory(gitHash)); +} + +function serverConfigResponseFactory(gitHash: string) { + return new ServerConfigResponse({ + version: "myConfigVersion", + gitHash: gitHash, + server: new ThirdPartyServerConfigResponse({ + name: "myThirdPartyServer", + url: "www.example.com", + }), + environment: new EnvironmentServerConfigResponse({ + vault: "vault.example.com", + }), + featureStates: { + feat1: "off", + feat2: "on", + feat3: "off", + }, + }); +} diff --git a/libs/common/src/platform/services/config/config.service.ts b/libs/common/src/platform/services/config/config.service.ts index 5b0325b68ac..c3a5fab4d70 100644 --- a/libs/common/src/platform/services/config/config.service.ts +++ b/libs/common/src/platform/services/config/config.service.ts @@ -1,103 +1,106 @@ -import { BehaviorSubject, concatMap, from, timer } from "rxjs"; +import { + ReplaySubject, + Subject, + concatMap, + delayWhen, + filter, + firstValueFrom, + from, + map, + merge, + timer, +} from "rxjs"; import { AuthService } from "../../../auth/abstractions/auth.service"; import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; -import { FeatureFlag } from "../../../enums/feature-flag.enum"; +import { FeatureFlag, FeatureFlagValue } from "../../../enums/feature-flag.enum"; import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-api.service.abstraction"; import { ConfigServiceAbstraction } from "../../abstractions/config/config.service.abstraction"; import { ServerConfig } from "../../abstractions/config/server-config"; -import { EnvironmentService } from "../../abstractions/environment.service"; +import { EnvironmentService, Region } from "../../abstractions/environment.service"; import { StateService } from "../../abstractions/state.service"; import { ServerConfigData } from "../../models/data/server-config.data"; +const ONE_HOUR_IN_MILLISECONDS = 1000 * 3600; + export class ConfigService implements ConfigServiceAbstraction { - protected _serverConfig = new BehaviorSubject(null); + protected _serverConfig = new ReplaySubject(1); serverConfig$ = this._serverConfig.asObservable(); + private _forceFetchConfig = new Subject(); + private inited = false; + + cloudRegion$ = this.serverConfig$.pipe( + map((config) => config?.environment?.cloudRegion ?? Region.US) + ); constructor( private stateService: StateService, private configApiService: ConfigApiServiceAbstraction, private authService: AuthService, - private environmentService: EnvironmentService - ) { - // Re-fetch the server config every hour - timer(0, 1000 * 3600) - .pipe(concatMap(() => from(this.fetchServerConfig()))) - .subscribe((serverConfig) => { - this._serverConfig.next(serverConfig); - }); + private environmentService: EnvironmentService, - this.environmentService.urls.subscribe(() => { - this.fetchServerConfig(); - }); - } + // Used to avoid duplicate subscriptions, e.g. in browser between the background and popup + private subscribe = true + ) {} - async fetchServerConfig(): Promise { - try { - const response = await this.configApiService.get(); - - if (response == null) { - return; - } - - const data = new ServerConfigData(response); - const serverConfig = new ServerConfig(data); - this._serverConfig.next(serverConfig); - - const userAuthStatus = await this.authService.getAuthStatus(); - if (userAuthStatus !== AuthenticationStatus.LoggedOut) { - // Store the config for offline use if the user is logged in - await this.stateService.setServerConfig(data); - this.environmentService.setCloudWebVaultUrl(data.environment?.cloudRegion); - } - // Always return new server config from server to calling method - // to ensure up to date information - // This change is specifically for the getFeatureFlag > buildServerConfig flow - // for locked or logged in users. - return serverConfig; - } catch { - return null; - } - } - - async getFeatureFlagBool(key: FeatureFlag, defaultValue = false): Promise { - return await this.getFeatureFlag(key, defaultValue); - } - - async getFeatureFlagString(key: FeatureFlag, defaultValue = ""): Promise { - return await this.getFeatureFlag(key, defaultValue); - } - - async getFeatureFlagNumber(key: FeatureFlag, defaultValue = 0): Promise { - return await this.getFeatureFlag(key, defaultValue); - } - - async getCloudRegion(defaultValue = "US"): Promise { - const serverConfig = await this.buildServerConfig(); - return serverConfig.environment?.cloudRegion ?? defaultValue; - } - - private async getFeatureFlag(key: FeatureFlag, defaultValue: T): Promise { - const serverConfig = await this.buildServerConfig(); - if ( - serverConfig == null || - serverConfig.featureStates == null || - serverConfig.featureStates[key] == null - ) { - return defaultValue; - } - return serverConfig.featureStates[key] as T; - } - - private async buildServerConfig(): Promise { - const data = await this.stateService.getServerConfig(); - const domain = data ? new ServerConfig(data) : this._serverConfig.getValue(); - - if (domain == null || !domain.isValid() || domain.expiresSoon()) { - const value = await this.fetchServerConfig(); - return value ?? domain; + init() { + if (!this.subscribe || this.inited) { + return; } - return domain; + // Get config from storage on initial load + const fromStorage = from(this.stateService.getServerConfig()).pipe( + map((data) => (data == null ? null : new ServerConfig(data))) + ); + + fromStorage.subscribe((config) => this._serverConfig.next(config)); + + // Fetch config from server + // If you need to fetch a new config when an event occurs, add an observable that emits on that event here + merge( + timer(ONE_HOUR_IN_MILLISECONDS, ONE_HOUR_IN_MILLISECONDS), // after 1 hour, then every hour + this.environmentService.urls, // when environment URLs change (including when app is started) + this._forceFetchConfig // manual + ) + .pipe( + delayWhen(() => fromStorage), // wait until storage has emitted first to avoid a race condition + concatMap(() => this.configApiService.get()), + filter((response) => response != null), + map((response) => new ServerConfigData(response)), + delayWhen((data) => this.saveConfig(data)), + map((data) => new ServerConfig(data)) + ) + .subscribe((config) => this._serverConfig.next(config)); + + this.inited = true; + } + + getFeatureFlag$(key: FeatureFlag, defaultValue?: T) { + return this.serverConfig$.pipe( + map((serverConfig) => { + if (serverConfig?.featureStates == null || serverConfig.featureStates[key] == null) { + return defaultValue; + } + + return serverConfig.featureStates[key] as T; + }) + ); + } + + async getFeatureFlag(key: FeatureFlag, defaultValue?: T) { + return await firstValueFrom(this.getFeatureFlag$(key, defaultValue)); + } + + triggerServerConfigFetch() { + this._forceFetchConfig.next(); + } + + private async saveConfig(data: ServerConfigData) { + if ((await this.authService.getAuthStatus()) === AuthenticationStatus.LoggedOut) { + return; + } + + await this.stateService.setServerConfig(data); + this.environmentService.setCloudWebVaultUrl(data.environment?.cloudRegion); } } diff --git a/libs/common/src/platform/services/environment.service.ts b/libs/common/src/platform/services/environment.service.ts index 770de20f834..d85341a68c2 100644 --- a/libs/common/src/platform/services/environment.service.ts +++ b/libs/common/src/platform/services/environment.service.ts @@ -1,4 +1,4 @@ -import { concatMap, Observable, Subject } from "rxjs"; +import { concatMap, Observable, ReplaySubject } from "rxjs"; import { EnvironmentUrls } from "../../auth/models/domain/environment-urls"; import { @@ -9,7 +9,7 @@ import { import { StateService } from "../abstractions/state.service"; export class EnvironmentService implements EnvironmentServiceAbstraction { - private readonly urlsSubject = new Subject(); + private readonly urlsSubject = new ReplaySubject(1); urls: Observable = this.urlsSubject.asObservable(); selectedRegion?: Region; initialized = false; From da06f1e5def7fe5f58968992ff47e9f9e829a2ac Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Fri, 8 Sep 2023 10:25:32 -0400 Subject: [PATCH 115/135] [PM-3612] Bug - Reprompt prevents autofill keyboard shortcut from cycling fill ciphers (#6096) * cycle last used cipher on subsequent keyboard shortcut use on a page * incorporate master password existence check * cycle next cipher before reprompt Co-authored-by: Cesar Gonzalez * replace hasMasterPassword with hasMasterPasswordAndMasterKeyHash --------- Co-authored-by: Cesar Gonzalez --- apps/browser/src/autofill/services/autofill.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 9d5dfb8a201..dc2a4918c3b 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -265,9 +265,14 @@ export default class AutofillService implements AutofillServiceInterface { } if ( - cipher.reprompt !== CipherRepromptType.None && + cipher.reprompt === CipherRepromptType.Password && + // If the master password has is not available, reprompt will error (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) ) { + if (fromCommand) { + this.cipherService.updateLastUsedIndexForUrl(tab.url); + } + await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { cipherId: cipher.id, action: "autofill", From d149894aadda3f85b4e46003cfc0022030c4991a Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 8 Sep 2023 18:38:46 +0200 Subject: [PATCH 116/135] [PM-2643] Resolve DUO iframe not being clickable (#6219) --- apps/desktop/src/index.html | 2 +- apps/desktop/src/scss/environment.scss | 26 ------------------- .../src/vault/app/vault/vault.component.ts | 2 -- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/apps/desktop/src/index.html b/apps/desktop/src/index.html index 6005361c0c7..23049d40d89 100644 --- a/apps/desktop/src/index.html +++ b/apps/desktop/src/index.html @@ -11,7 +11,7 @@ Bitwarden - +
    diff --git a/apps/desktop/src/scss/environment.scss b/apps/desktop/src/scss/environment.scss index b18032f1a71..eae8b4a2d5b 100644 --- a/apps/desktop/src/scss/environment.scss +++ b/apps/desktop/src/scss/environment.scss @@ -1,30 +1,4 @@ html.os_macos { - body.layout_frontend { - -webkit-app-region: drag; - - button, - a, - i, - b, - span, - input, - p, - h1, - h2, - h3, - h4, - h5, - h6, - img, - select, - textarea, - label, - .box, - .modal-backdrop { - -webkit-app-region: no-drag; - } - } - .vault .header-search { -webkit-app-region: drag; diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index a058f080543..194cee098f7 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -207,7 +207,6 @@ export class VaultComponent implements OnInit, OnDestroy { if (!this.syncService.syncInProgress) { await this.load(); } - document.body.classList.remove("layout_frontend"); this.searchBarService.setEnabled(true); this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); @@ -226,7 +225,6 @@ export class VaultComponent implements OnInit, OnDestroy { ngOnDestroy() { this.searchBarService.setEnabled(false); this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); - document.body.classList.add("layout_frontend"); } async load() { From 93c11a7dab944e919ad4522a91bf8f1ac86a053b Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:37:46 +0100 Subject: [PATCH 117/135] Setting properly the type to verify on SendAddEdit (#6252) --- apps/browser/src/tools/popup/send/send-add-edit.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.html b/apps/browser/src/tools/popup/send/send-add-edit.component.html index 707adaa7a55..11dca222fca 100644 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.html +++ b/apps/browser/src/tools/popup/send/send-add-edit.component.html @@ -27,7 +27,7 @@ icon="bwi-external-link bwi-rotate-270 bwi-fw" [clickable]="true" title="{{ 'sendFileCalloutHeader' | i18n }}" - *ngIf="showFilePopoutMessage && send.type === sendType.File && !disableSend" + *ngIf="showFilePopoutMessage && type === sendType.File && !disableSend" (click)="popOutWindow()" >
    {{ "sendLinuxChromiumFileWarning" | i18n }}
    From 30d8168c2e5c96739a4c02fa7dc7306911198d32 Mon Sep 17 00:00:00 2001 From: Robyn MacCallum Date: Mon, 11 Sep 2023 10:40:50 -0400 Subject: [PATCH 118/135] [PM-3600] Try to get key in master password reprompt check (#6087) * Add check to hasMasterPasswordAndMasterKeyHash to make sure getMasterKey() has a value * Try getting or deriving the key on password reprompt --- .../src/vault/components/password-reprompt.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/angular/src/vault/components/password-reprompt.component.ts b/libs/angular/src/vault/components/password-reprompt.component.ts index 0f0fd685b20..1094b379316 100644 --- a/libs/angular/src/vault/components/password-reprompt.component.ts +++ b/libs/angular/src/vault/components/password-reprompt.component.ts @@ -27,7 +27,8 @@ export class PasswordRepromptComponent { } async submit() { - if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null))) { + const storedMasterKey = await this.cryptoService.getOrDeriveMasterKey(this.masterPassword); + if (!(await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, storedMasterKey))) { this.platformUtilsService.showToast( "error", this.i18nService.t("errorOccurred"), From 2323509dee449e33f55c4b1a7106679f37b56d7c Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Mon, 11 Sep 2023 16:41:34 +0200 Subject: [PATCH 119/135] [PM-147] Import error states usability improvements (#6245) * Add import error dialog (cherry picked from commit 518211dae0c2e7d26ea6dbfec77a9066c4262c75) * Rename ErrorList to ErrorListItem (cherry picked from commit a7dd643710fa6d68d3d7bb427437dbb84e98a1f2) --- .../dialog/import-error-dialog.component.html | 29 ++++++++++++++++ .../dialog/import-error-dialog.component.ts | 33 +++++++++++++++++++ .../app/tools/import-export/dialog/index.ts | 1 + .../import-export/import-export.module.ts | 7 +++- .../tools/import-export/import.component.ts | 32 +++++------------- 5 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.html create mode 100644 apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.ts diff --git a/apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.html b/apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.html new file mode 100644 index 00000000000..c1ad8b53932 --- /dev/null +++ b/apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.html @@ -0,0 +1,29 @@ + + + {{ "importError" | i18n }} + + + +
    {{ "resolveTheErrorsBelowAndTryAgain" | i18n }}
    + + + + {{ "name" | i18n }} + {{ "description" | i18n }} + + + + + {{ r.type }} + {{ r.message }} + + + +
    + +
    + +
    +
    diff --git a/apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.ts b/apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.ts new file mode 100644 index 00000000000..abb68cf53b1 --- /dev/null +++ b/apps/web/src/app/tools/import-export/dialog/import-error-dialog.component.ts @@ -0,0 +1,33 @@ +import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject, OnInit } from "@angular/core"; + +import { TableDataSource } from "@bitwarden/components"; + +export interface ErrorListItem { + type: string; + message: string; +} + +@Component({ + selector: "app-import-error-dialog", + templateUrl: "./import-error-dialog.component.html", +}) +export class ImportErrorDialogComponent implements OnInit { + protected dataSource = new TableDataSource(); + + constructor(public dialogRef: DialogRef, @Inject(DIALOG_DATA) public data: Error) {} + + ngOnInit(): void { + const split = this.data.message.split("\n\n"); + if (split.length == 1) { + this.dataSource.data = [{ type: "", message: this.data.message }]; + return; + } + + const data: ErrorListItem[] = []; + split.forEach((line) => { + data.push({ type: "", message: line }); + }); + this.dataSource.data = data; + } +} diff --git a/apps/web/src/app/tools/import-export/dialog/index.ts b/apps/web/src/app/tools/import-export/dialog/index.ts index 7ad42fe1db9..641cd6600a1 100644 --- a/apps/web/src/app/tools/import-export/dialog/index.ts +++ b/apps/web/src/app/tools/import-export/dialog/index.ts @@ -1,2 +1,3 @@ +export * from "./import-error-dialog.component"; export * from "./import-success-dialog.component"; export * from "./file-password-prompt.component"; diff --git a/apps/web/src/app/tools/import-export/import-export.module.ts b/apps/web/src/app/tools/import-export/import-export.module.ts index 4d59f412367..d180417cdcc 100644 --- a/apps/web/src/app/tools/import-export/import-export.module.ts +++ b/apps/web/src/app/tools/import-export/import-export.module.ts @@ -15,7 +15,11 @@ import { import { LooseComponentsModule, SharedModule } from "../../shared"; -import { ImportSuccessDialogComponent, FilePasswordPromptComponent } from "./dialog"; +import { + ImportErrorDialogComponent, + ImportSuccessDialogComponent, + FilePasswordPromptComponent, +} from "./dialog"; import { ExportComponent } from "./export.component"; import { ImportExportRoutingModule } from "./import-export-routing.module"; import { ImportComponent } from "./import.component"; @@ -26,6 +30,7 @@ import { ImportComponent } from "./import.component"; ImportComponent, ExportComponent, FilePasswordPromptComponent, + ImportErrorDialogComponent, ImportSuccessDialogComponent, ], providers: [ diff --git a/apps/web/src/app/tools/import-export/import.component.ts b/apps/web/src/app/tools/import-export/import.component.ts index eb9201f021d..fb7d1d5501d 100644 --- a/apps/web/src/app/tools/import-export/import.component.ts +++ b/apps/web/src/app/tools/import-export/import.component.ts @@ -4,7 +4,6 @@ import { Router } from "@angular/router"; import * as JSZip from "jszip"; import { concat, Observable, Subject, lastValueFrom, combineLatest } from "rxjs"; import { map, takeUntil } from "rxjs/operators"; -import Swal, { SweetAlertIcon } from "sweetalert2"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { @@ -31,7 +30,11 @@ import { ImportType, } from "@bitwarden/importer"; -import { FilePasswordPromptComponent, ImportSuccessDialogComponent } from "./dialog"; +import { + FilePasswordPromptComponent, + ImportErrorDialogComponent, + ImportSuccessDialogComponent, +} from "./dialog"; @Component({ selector: "app-import", @@ -247,7 +250,9 @@ export class ImportComponent implements OnInit, OnDestroy { this.syncService.fullSync(true); await this.onSuccessfulImport(); } catch (e) { - this.error(e); + this.dialogService.open(ImportErrorDialogComponent, { + data: e, + }); this.logService.error(e); } } @@ -303,27 +308,6 @@ export class ImportComponent implements OnInit, OnDestroy { this.fileSelected = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; } - private async error(error: Error) { - await Swal.fire({ - heightAuto: false, - buttonsStyling: false, - icon: "error" as SweetAlertIcon, - iconHtml: ``, - input: "textarea", - inputValue: error.message, - inputAttributes: { - readonly: "true", - }, - titleText: this.i18nService.t("importError"), - text: this.i18nService.t("importErrorDesc"), - showConfirmButton: true, - confirmButtonText: this.i18nService.t("ok"), - onOpen: (popupEl) => { - popupEl.querySelector(".swal2-textarea").scrollTo(0, 0); - }, - }); - } - private getFileContents(file: File): Promise { if (this.format === "1password1pux") { return this.extractZipContent(file, "export.data"); From f999e2cea96695df8458460a91a88b0dc8f53594 Mon Sep 17 00:00:00 2001 From: Will Martin Date: Mon, 11 Sep 2023 14:54:23 -0400 Subject: [PATCH 120/135] [PM-3763] remove Sweet Alert from desktop and browser (#6138) * update desktop and browser swal references to use CL * rename bit-dialog-close * share fingerprint dialog between desktop and browser * apply code review * format fingerprint in template * apply code review * fix button color * fix button types * update var names * close awaitDesktop dialog on success AND error * add DialogService to NativeMessageHandlerService deps * wrap browser message dialogs in ngZone.run * wrap native messaging handler in ngzone.run * apply code review * fix async ngzone --------- Co-authored-by: Daniel James Smith --- .../background/nativeMessaging.background.ts | 7 +-- apps/browser/src/popup/app.component.ts | 24 +++------ ...op-sync-verification-dialog.component.html | 18 +++++++ ...ktop-sync-verification-dialog.component.ts | 24 +++++++++ .../await-desktop-dialog.component.html | 11 ++++ .../await-desktop-dialog.component.ts | 17 ++++++ .../src/popup/settings/settings.component.ts | 47 +++++------------ apps/desktop/src/app/app.component.ts | 15 ++---- ...er-sync-verification-dialog.component.html | 19 +++++++ ...wser-sync-verification-dialog.component.ts | 25 +++++++++ ...ify-native-messaging-dialog.component.html | 18 +++++++ ...erify-native-messaging-dialog.component.ts | 24 +++++++++ .../src/app/services/services.module.ts | 1 + .../native-message-handler.service.ts | 26 ++++------ .../src/services/native-messaging.service.ts | 52 +++++++++---------- .../manage/group-add-edit.component.html | 8 +-- .../fingerprint-dialog.component.html | 23 ++++++++ .../fingerprint-dialog.component.ts | 22 ++++++++ libs/auth/src/index.ts | 1 + .../directives/dialog-close.directive.ts | 4 +- 20 files changed, 268 insertions(+), 118 deletions(-) create mode 100644 apps/browser/src/popup/components/desktop-sync-verification-dialog.component.html create mode 100644 apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts create mode 100644 apps/browser/src/popup/settings/await-desktop-dialog.component.html create mode 100644 apps/browser/src/popup/settings/await-desktop-dialog.component.ts create mode 100644 apps/desktop/src/app/components/browser-sync-verification-dialog.component.html create mode 100644 apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts create mode 100644 apps/desktop/src/app/components/verify-native-messaging-dialog.component.html create mode 100644 apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts create mode 100644 libs/auth/src/components/fingerprint-dialog.component.html create mode 100644 libs/auth/src/components/fingerprint-dialog.component.ts diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index d393022b7b9..88fd81a3a70 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -422,9 +422,10 @@ export class NativeMessagingBackground { } private async showFingerprintDialog() { - const fingerprint = ( - await this.cryptoService.getFingerprint(await this.stateService.getUserId(), this.publicKey) - ).join(" "); + const fingerprint = await this.cryptoService.getFingerprint( + await this.stateService.getUserId(), + this.publicKey + ); this.messagingService.send("showNativeMessagingFinterprintDialog", { fingerprint: fingerprint, diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 815109c5499..4af5b49b52e 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -9,8 +9,7 @@ import { import { DomSanitizer } from "@angular/platform-browser"; import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; import { IndividualConfig, ToastrService } from "ngx-toastr"; -import { filter, concatMap, Subject, takeUntil } from "rxjs"; -import Swal from "sweetalert2"; +import { filter, concatMap, Subject, takeUntil, firstValueFrom } from "rxjs"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; @@ -23,6 +22,7 @@ import { BrowserApi } from "../platform/browser/browser-api"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; import { routerTransition } from "./app-routing.animations"; +import { DesktopSyncVerificationDialogComponent } from "./components/desktop-sync-verification-dialog.component"; @Component({ selector: "app-root", @@ -113,10 +113,10 @@ export class AppComponent implements OnInit, OnDestroy { }); } } else if (msg.command === "showDialog") { - await this.showDialog(msg); + await this.ngZone.run(() => this.showDialog(msg)); } else if (msg.command === "showNativeMessagingFinterprintDialog") { // TODO: Should be refactored to live in another service. - await this.showNativeMessagingFingerprintDialog(msg); + await this.ngZone.run(() => this.showNativeMessagingFingerprintDialog(msg)); } else if (msg.command === "showToast") { this.ngZone.run(() => { this.showToast(msg); @@ -242,19 +242,11 @@ export class AppComponent implements OnInit, OnDestroy { } private async showNativeMessagingFingerprintDialog(msg: any) { - await Swal.fire({ - heightAuto: false, - buttonsStyling: false, - icon: "warning", - iconHtml: '', - html: `${this.i18nService.t("desktopIntegrationVerificationText")}

    ${ - msg.fingerprint - }`, - titleText: this.i18nService.t("desktopSyncVerificationTitle"), - showConfirmButton: true, - confirmButtonText: this.i18nService.t("ok"), - timer: 300000, + const dialogRef = DesktopSyncVerificationDialogComponent.open(this.dialogService, { + fingerprint: msg.fingerprint, }); + + return firstValueFrom(dialogRef.closed); } private async clearComponentStates() { diff --git a/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.html b/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.html new file mode 100644 index 00000000000..a2a2cd97805 --- /dev/null +++ b/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.html @@ -0,0 +1,18 @@ + + + {{ "desktopSyncVerificationTitle" | i18n }} + + +

    + {{ "desktopIntegrationVerificationText" | i18n }} +

    +

    + {{ params.fingerprint.join("-") }} +

    +
    + + + +
    diff --git a/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts b/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts new file mode 100644 index 00000000000..c860ef1e342 --- /dev/null +++ b/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts @@ -0,0 +1,24 @@ +import { DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; + +export type DesktopSyncVerificationDialogParams = { + fingerprint: string[]; +}; + +@Component({ + templateUrl: "desktop-sync-verification-dialog.component.html", + standalone: true, + imports: [JslibModule, ButtonModule, DialogModule], +}) +export class DesktopSyncVerificationDialogComponent { + constructor(@Inject(DIALOG_DATA) protected params: DesktopSyncVerificationDialogParams) {} + + static open(dialogService: DialogService, data: DesktopSyncVerificationDialogParams) { + return dialogService.open(DesktopSyncVerificationDialogComponent, { + data, + }); + } +} diff --git a/apps/browser/src/popup/settings/await-desktop-dialog.component.html b/apps/browser/src/popup/settings/await-desktop-dialog.component.html new file mode 100644 index 00000000000..688071a15d6 --- /dev/null +++ b/apps/browser/src/popup/settings/await-desktop-dialog.component.html @@ -0,0 +1,11 @@ + + {{ "awaitDesktop" | i18n }} + + {{ "awaitDesktopDesc" | i18n }} + + + + + diff --git a/apps/browser/src/popup/settings/await-desktop-dialog.component.ts b/apps/browser/src/popup/settings/await-desktop-dialog.component.ts new file mode 100644 index 00000000000..9ed6efe036f --- /dev/null +++ b/apps/browser/src/popup/settings/await-desktop-dialog.component.ts @@ -0,0 +1,17 @@ +import { Component } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; + +@Component({ + templateUrl: "await-desktop-dialog.component.html", + standalone: true, + imports: [JslibModule, ButtonModule, DialogModule], +}) +export class AwaitDesktopDialogComponent { + static open(dialogService: DialogService) { + return dialogService.open(AwaitDesktopDialogComponent, { + disableClose: true, + }); + } +} diff --git a/apps/browser/src/popup/settings/settings.component.ts b/apps/browser/src/popup/settings/settings.component.ts index 30a1f03eacf..efeb6aba6d2 100644 --- a/apps/browser/src/popup/settings/settings.component.ts +++ b/apps/browser/src/popup/settings/settings.component.ts @@ -15,9 +15,9 @@ import { switchMap, takeUntil, } from "rxjs"; -import Swal from "sweetalert2"; import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { FingerprintDialogComponent } from "@bitwarden/auth"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -39,6 +39,7 @@ import { SetPinComponent } from "../components/set-pin.component"; import { PopupUtilsService } from "../services/popup-utils.service"; import { AboutComponent } from "./about.component"; +import { AwaitDesktopDialogComponent } from "./await-desktop-dialog.component"; const RateUrls = { [DeviceType.ChromeExtension]: @@ -361,25 +362,15 @@ export class SettingsComponent implements OnInit { return; } - const submitted = Swal.fire({ - heightAuto: false, - buttonsStyling: false, - titleText: this.i18nService.t("awaitDesktop"), - text: this.i18nService.t("awaitDesktopDesc"), - icon: "info", - iconHtml: '', - showCancelButton: true, - cancelButtonText: this.i18nService.t("cancel"), - showConfirmButton: false, - allowOutsideClick: false, - }); + const awaitDesktopDialogRef = AwaitDesktopDialogComponent.open(this.dialogService); + const awaitDesktopDialogClosed = firstValueFrom(awaitDesktopDialogRef.closed); await this.stateService.setBiometricAwaitingAcceptance(true); await this.cryptoService.refreshAdditionalKeys(); await Promise.race([ - submitted.then(async (result) => { - if (result.dismiss === Swal.DismissReason.cancel) { + awaitDesktopDialogClosed.then(async (result) => { + if (result) { this.form.controls.biometric.setValue(false); await this.stateService.setBiometricAwaitingAcceptance(null); } @@ -388,8 +379,6 @@ export class SettingsComponent implements OnInit { .authenticateBiometric() .then((result) => { this.form.controls.biometric.setValue(result); - - Swal.close(); if (!result) { this.platformUtilsService.showToast( "error", @@ -411,6 +400,9 @@ export class SettingsComponent implements OnInit { cancelButtonText: null, type: "danger", }); + }) + .finally(() => { + awaitDesktopDialogRef.close(false); }), ]); } else { @@ -497,27 +489,12 @@ export class SettingsComponent implements OnInit { const fingerprint = await this.cryptoService.getFingerprint( await this.stateService.getUserId() ); - const p = document.createElement("p"); - p.innerText = this.i18nService.t("yourAccountsFingerprint") + ":"; - const p2 = document.createElement("p"); - p2.innerText = fingerprint.join("-"); - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(p2); - const result = await Swal.fire({ - heightAuto: false, - buttonsStyling: false, - html: div, - showCancelButton: true, - cancelButtonText: this.i18nService.t("close"), - showConfirmButton: true, - confirmButtonText: this.i18nService.t("learnMore"), + const dialogRef = FingerprintDialogComponent.open(this.dialogService, { + fingerprint, }); - if (result.value) { - this.platformUtilsService.launchUri("https://bitwarden.com/help/fingerprint-phrase/"); - } + return firstValueFrom(dialogRef.closed); } rate() { diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 39e2d2f8b11..34262c3a309 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -16,6 +16,7 @@ import { firstValueFrom, Subject, takeUntil } from "rxjs"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { FingerprintDialogComponent } from "@bitwarden/auth"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; @@ -244,18 +245,8 @@ export class AppComponent implements OnInit, OnDestroy { const fingerprint = await this.cryptoService.getFingerprint( await this.stateService.getUserId() ); - const result = await this.dialogService.openSimpleDialog({ - title: { key: "fingerprintPhrase" }, - content: - this.i18nService.t("yourAccountsFingerprint") + ":\n" + fingerprint.join("-"), - acceptButtonText: { key: "learnMore" }, - cancelButtonText: { key: "close" }, - type: "info", - }); - - if (result) { - this.platformUtilsService.launchUri("https://bitwarden.com/help/fingerprint-phrase/"); - } + const dialogRef = FingerprintDialogComponent.open(this.dialogService, { fingerprint }); + await firstValueFrom(dialogRef.closed); break; } case "deleteAccount": diff --git a/apps/desktop/src/app/components/browser-sync-verification-dialog.component.html b/apps/desktop/src/app/components/browser-sync-verification-dialog.component.html new file mode 100644 index 00000000000..63bffe9c4e0 --- /dev/null +++ b/apps/desktop/src/app/components/browser-sync-verification-dialog.component.html @@ -0,0 +1,19 @@ + + + {{ "verifyBrowserTitle" | i18n }} + + +

    {{ "verifyBrowserDesc" | i18n }}

    +

    + {{ params.fingerprint.join("-") }} +

    +
    + + + + +
    diff --git a/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts b/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts new file mode 100644 index 00000000000..aefa5672a94 --- /dev/null +++ b/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts @@ -0,0 +1,25 @@ +import { DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; + +export type BrowserSyncVerificationDialogParams = { + fingerprint: string[]; +}; + +@Component({ + templateUrl: "browser-sync-verification-dialog.component.html", + standalone: true, + imports: [JslibModule, ButtonModule, DialogModule], +}) +export class BrowserSyncVerificationDialogComponent { + constructor(@Inject(DIALOG_DATA) protected params: BrowserSyncVerificationDialogParams) {} + + static open(dialogService: DialogService, data: BrowserSyncVerificationDialogParams) { + return dialogService.open(BrowserSyncVerificationDialogComponent, { + data, + disableClose: true, + }); + } +} diff --git a/apps/desktop/src/app/components/verify-native-messaging-dialog.component.html b/apps/desktop/src/app/components/verify-native-messaging-dialog.component.html new file mode 100644 index 00000000000..0334857962b --- /dev/null +++ b/apps/desktop/src/app/components/verify-native-messaging-dialog.component.html @@ -0,0 +1,18 @@ + + + {{ "verifyNativeMessagingConnectionTitle" | i18n : data.applicationName }}: + + + {{ "verifyNativeMessagingConnectionDesc" | i18n }} +
    + {{ "verifyNativeMessagingConnectionWarning" | i18n }} +
    + + + + +
    diff --git a/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts b/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts new file mode 100644 index 00000000000..507d566336b --- /dev/null +++ b/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts @@ -0,0 +1,24 @@ +import { DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; + +export type VerifyNativeMessagingDialogData = { + applicationName: string; +}; + +@Component({ + templateUrl: "verify-native-messaging-dialog.component.html", + standalone: true, + imports: [JslibModule, ButtonModule, DialogModule], +}) +export class VerifyNativeMessagingDialogComponent { + constructor(@Inject(DIALOG_DATA) protected data: VerifyNativeMessagingDialogData) {} + + static open(dialogService: DialogService, data: VerifyNativeMessagingDialogData) { + return dialogService.open(VerifyNativeMessagingDialogComponent, { + data, + }); + } +} diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 42208077c33..87fb1eecfcb 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -169,6 +169,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); MessagingServiceAbstraction, I18nServiceAbstraction, EncryptedMessageHandlerService, + DialogService, ], }, { diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts index 9f5f1d460df..6779195c3f1 100644 --- a/apps/desktop/src/services/native-message-handler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import { ipcRenderer } from "electron"; -import Swal from "sweetalert2"; +import { firstValueFrom } from "rxjs"; import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -11,7 +11,9 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { StateService } from "@bitwarden/common/platform/services/state.service"; +import { DialogService } from "@bitwarden/components"; +import { VerifyNativeMessagingDialogComponent } from "../app/components/verify-native-messaging-dialog.component"; import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; import { EncryptedMessage } from "../models/native-messaging/encrypted-message"; import { EncryptedMessageResponse } from "../models/native-messaging/encrypted-message-response"; @@ -33,7 +35,8 @@ export class NativeMessageHandlerService { private cryptoFunctionService: CryptoFunctionService, private messagingService: MessagingService, private i18nService: I18nService, - private encryptedMessageHandlerService: EncryptedMessageHandlerService + private encryptedMessageHandlerService: EncryptedMessageHandlerService, + private dialogService: DialogService ) {} async handleMessage(message: Message) { @@ -87,21 +90,12 @@ export class NativeMessageHandlerService { // Ask for confirmation from user this.messagingService.send("setFocus"); - const submitted = await Swal.fire({ - heightAuto: false, - titleText: this.i18nService.t("verifyNativeMessagingConnectionTitle", applicationName), - html: `${this.i18nService.t("verifyNativeMessagingConnectionDesc")}
    ${this.i18nService.t( - "verifyNativeMessagingConnectionWarning" - )}`, - showCancelButton: true, - cancelButtonText: this.i18nService.t("no"), - showConfirmButton: true, - confirmButtonText: this.i18nService.t("yes"), - allowOutsideClick: false, - focusCancel: true, - }); - if (submitted.value !== true) { + const nativeMessagingVerified = await firstValueFrom( + VerifyNativeMessagingDialogComponent.open(this.dialogService, { applicationName }).closed + ); + + if (nativeMessagingVerified !== true) { this.sendResponse({ messageId: messageId, version: NativeMessagingVersion.Latest, diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 3928778f313..c18a67e6b43 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -1,7 +1,6 @@ -import { Injectable } from "@angular/core"; +import { Injectable, NgZone } from "@angular/core"; import { ipcRenderer } from "electron"; import { firstValueFrom } from "rxjs"; -import Swal from "sweetalert2"; import { KeySuffixOptions } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -14,7 +13,9 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv 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 { DialogService } from "@bitwarden/components"; +import { BrowserSyncVerificationDialogComponent } from "../app/components/browser-sync-verification-dialog.component"; import { LegacyMessage } from "../models/native-messaging/legacy-message"; import { LegacyMessageWrapper } from "../models/native-messaging/legacy-message-wrapper"; import { Message } from "../models/native-messaging/message"; @@ -36,7 +37,9 @@ export class NativeMessagingService { private i18nService: I18nService, private messagingService: MessagingService, private stateService: StateService, - private nativeMessageHandler: NativeMessageHandlerService + private nativeMessageHandler: NativeMessageHandlerService, + private dialogService: DialogService, + private ngZone: NgZone ) {} init() { @@ -69,27 +72,20 @@ export class NativeMessagingService { if (await this.stateService.getEnableBrowserIntegrationFingerprint()) { ipcRenderer.send("nativeMessagingReply", { command: "verifyFingerprint", appId: appId }); - const fingerprint = ( - await this.cryptoService.getFingerprint( - await this.stateService.getUserId(), - remotePublicKey - ) - ).join(" "); + const fingerprint = await this.cryptoService.getFingerprint( + await this.stateService.getUserId(), + remotePublicKey + ); this.messagingService.send("setFocus"); - // Await confirmation that fingerprint is correct - const submitted = await Swal.fire({ - titleText: this.i18nService.t("verifyBrowserTitle"), - html: `${this.i18nService.t("verifyBrowserDesc")}

    ${fingerprint}`, - showCancelButton: true, - cancelButtonText: this.i18nService.t("cancel"), - showConfirmButton: true, - confirmButtonText: this.i18nService.t("approve"), - allowOutsideClick: false, - }); + const dialogRef = this.ngZone.run(() => + BrowserSyncVerificationDialogComponent.open(this.dialogService, { fingerprint }) + ); - if (submitted.value !== true) { + const browserSyncVerified = await firstValueFrom(dialogRef.closed); + + if (browserSyncVerified !== true) { return; } } @@ -127,13 +123,15 @@ export class NativeMessagingService { if (!(await this.stateService.getBiometricUnlock({ userId: message.userId }))) { this.send({ command: "biometricUnlock", response: "not enabled" }, appId); - return await Swal.fire({ - title: this.i18nService.t("biometricsNotEnabledTitle"), - text: this.i18nService.t("biometricsNotEnabledDesc"), - showCancelButton: true, - cancelButtonText: this.i18nService.t("cancel"), - showConfirmButton: false, - }); + return this.ngZone.run(() => + this.dialogService.openSimpleDialog({ + type: "warning", + title: { key: "biometricsNotEnabledTitle" }, + content: { key: "biometricsNotEnabledDesc" }, + cancelButtonText: null, + acceptButtonText: { key: "cancel" }, + }) + ); } const userKey = await this.cryptoService.getUserKeyFromStorage( diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html index 4978842fb4d..b6175d80297 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html @@ -69,13 +69,7 @@ - + + diff --git a/libs/auth/src/components/fingerprint-dialog.component.ts b/libs/auth/src/components/fingerprint-dialog.component.ts new file mode 100644 index 00000000000..2a7b3e10997 --- /dev/null +++ b/libs/auth/src/components/fingerprint-dialog.component.ts @@ -0,0 +1,22 @@ +import { DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; + +export type FingerprintDialogData = { + fingerprint: string[]; +}; + +@Component({ + templateUrl: "fingerprint-dialog.component.html", + standalone: true, + imports: [JslibModule, ButtonModule, DialogModule], +}) +export class FingerprintDialogComponent { + constructor(@Inject(DIALOG_DATA) protected data: FingerprintDialogData) {} + + static open(dialogService: DialogService, data: FingerprintDialogData) { + return dialogService.open(FingerprintDialogComponent, { data }); + } +} diff --git a/libs/auth/src/index.ts b/libs/auth/src/index.ts index e69de29bb2d..0b4f07fc29d 100644 --- a/libs/auth/src/index.ts +++ b/libs/auth/src/index.ts @@ -0,0 +1 @@ +export * from "./components/fingerprint-dialog.component"; diff --git a/libs/components/src/dialog/directives/dialog-close.directive.ts b/libs/components/src/dialog/directives/dialog-close.directive.ts index a45991bb5e6..543c37715dd 100644 --- a/libs/components/src/dialog/directives/dialog-close.directive.ts +++ b/libs/components/src/dialog/directives/dialog-close.directive.ts @@ -5,9 +5,9 @@ import { Directive, HostListener, Input, Optional } from "@angular/core"; selector: "[bitDialogClose]", }) export class DialogCloseDirective { - @Input("bit-dialog-close") dialogResult: any; + @Input("bitDialogClose") dialogResult: any; - constructor(@Optional() public dialogRef: DialogRef) {} + constructor(@Optional() public dialogRef: DialogRef) {} @HostListener("click") close(): void { this.dialogRef.close(this.dialogResult); From 373569833fe3d892339ef51f0c8224aa1071f62b Mon Sep 17 00:00:00 2001 From: Will Martin Date: Mon, 11 Sep 2023 17:15:27 -0400 Subject: [PATCH 121/135] [PS-2841] remove SweetAlert2 dependency (#6191) * remove swal from app.component * remove dep * remove unused swal css --- apps/browser/src/popup/scss/misc.scss | 3 +- apps/browser/src/popup/scss/plugins.scss | 114 ----------------------- apps/browser/webpack.config.js | 4 - apps/desktop/src/scss/plugins.scss | 114 ----------------------- apps/web/src/app/app.component.ts | 6 -- apps/web/src/scss/base.scss | 2 - apps/web/src/scss/buttons.scss | 9 +- apps/web/src/scss/plugins.scss | 90 ------------------ apps/web/src/scss/styles.scss | 1 - apps/web/webpack.config.js | 4 - package-lock.json | 9 -- package.json | 1 - 12 files changed, 4 insertions(+), 353 deletions(-) diff --git a/apps/browser/src/popup/scss/misc.scss b/apps/browser/src/popup/scss/misc.scss index 07f7b6d5091..c106931c1fc 100644 --- a/apps/browser/src/popup/scss/misc.scss +++ b/apps/browser/src/popup/scss/misc.scss @@ -422,8 +422,7 @@ img, .callout, .row-label, .modal-title, -.overlay-container, -.swal2-container { +.overlay-container { user-select: none; } diff --git a/apps/browser/src/popup/scss/plugins.scss b/apps/browser/src/popup/scss/plugins.scss index 44a181c0dcc..104927d73bb 100644 --- a/apps/browser/src/popup/scss/plugins.scss +++ b/apps/browser/src/popup/scss/plugins.scss @@ -1,5 +1,4 @@ @import "~ngx-toastr/toastr"; -@import "~#sweetalert2"; @import "variables.scss"; @import "buttons.scss"; @@ -98,119 +97,6 @@ } } -// SweetAlert - -.swal2-popup { - padding: 15px; - border-radius: $border-radius; - width: 34em; - - @include themify($themes) { - background-color: themed("backgroundColorAlt"); - color: themed("textColor"); - } - - .swal2-icon { - margin: 0 auto; - width: auto; - height: auto; - border: none; - } - - .swal2-content { - margin: 0; - font-size: $font-size-base; - @include themify($themes) { - color: themed("textColor"); - } - - label.checkbox { - margin-top: 10px; - display: flex; - text-align: left; - align-items: top; - - input { - margin: 3px 5px 0 1px; - } - } - - .swal2-input, - .swal2-textarea { - border: 1px solid #000000; - border-radius: $border-radius; - margin-bottom: 0; - box-shadow: none; - // Inherit theme font-size - font-size: inherit; - - // Sweetalert 1 did not have box-shadow - &:focus { - box-shadow: none; - } - @include themify($themes) { - border-color: themed("inputBorderColor"); - color: themed("textColor"); - background-color: themed("inputBackgroundColor"); - } - &::-webkit-input-placeholder { - @include themify($themes) { - color: themed("inputPlaceholderColor"); - } - } - } - } - - i.swal-custom-icon { - display: block; - margin: 0 auto; - font-size: 35px; - } - - .swal2-title { - padding: 10px 0 15px; - margin: 0; - font-size: $font-size-large; - - @include themify($themes) { - color: themed("textColor"); - } - } - - .swal2-text { - text-align: left; // sweetalert1 behaviour - font-size: $font-size-base; - - @include themify($themes) { - color: themed("textColor"); - } - } - - > .swal2-text:first-child { - margin-top: 20px; - } - - .swal2-actions { - margin: 20px auto 0; - justify-content: flex-start; - flex-direction: row-reverse; - - button { - margin-left: 10px; - @extend .btn; - - &.swal2-confirm { - @extend .btn, .primary; - font-weight: bold; - } - } - } - - .swal2-validation-message { - margin-top: 20px; - } -} - date-input-polyfill { &[data-open="true"] { z-index: 10000 !important; diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index 23fba305b6e..834647231f3 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -213,10 +213,6 @@ const mainConfig = { extensions: [".ts", ".js"], symlinks: false, modules: [path.resolve("../../node_modules")], - alias: { - sweetalert2: require.resolve("sweetalert2/dist/sweetalert2.js"), - "#sweetalert2": require.resolve("sweetalert2/src/sweetalert2.scss"), - }, fallback: { assert: false, buffer: require.resolve("buffer/"), diff --git a/apps/desktop/src/scss/plugins.scss b/apps/desktop/src/scss/plugins.scss index aed093d53be..c156456809c 100644 --- a/apps/desktop/src/scss/plugins.scss +++ b/apps/desktop/src/scss/plugins.scss @@ -1,5 +1,4 @@ @import "~ngx-toastr/toastr"; -@import "~sweetalert2/src/sweetalert2.scss"; @import "variables.scss"; @@ -94,116 +93,3 @@ } } } - -// SweetAlert2 - -.swal2-popup { - padding: 15px; - border-radius: $border-radius; - width: 34em; - - @include themify($themes) { - background-color: themed("backgroundColorAlt"); - color: themed("textColor"); - } - - .swal2-icon { - margin: 0 auto; - width: auto; - height: auto; - border: none; - } - - .swal2-content { - margin: 0; - font-size: $font-size-base; - @include themify($themes) { - color: themed("textColor"); - } - - label.checkbox { - margin-top: 10px; - display: flex; - text-align: left; - align-items: top; - - input { - margin: 3px 5px 0 1px; - } - } - - .swal2-input, - .swal2-textarea { - border: 1px solid #000000; - border-radius: $border-radius; - margin-bottom: 0; - box-shadow: none; - // Inherit theme font-size - font-size: inherit; - - // Sweetalert 1 did not have box-shadow - &:focus { - box-shadow: none; - } - @include themify($themes) { - border-color: themed("inputBorderColor"); - color: themed("textColor"); - background-color: themed("inputBackgroundColor"); - } - &::-webkit-input-placeholder { - @include themify($themes) { - color: themed("inputPlaceholderColor"); - } - } - } - } - - i.swal-custom-icon { - display: block; - margin: 0 auto; - font-size: 35px; - } - - .swal2-title { - padding: 10px 0 15px; - margin: 0; - font-size: $font-size-large; - - @include themify($themes) { - color: themed("textColor"); - } - } - - .swal2-text { - text-align: left; // sweetalert1 behaviour - font-size: $font-size-base; - - @include themify($themes) { - color: themed("textColor"); - } - } - - > .swal2-text:first-child { - margin-top: 20px; - } - - .swal2-actions { - margin: 20px auto 0; - justify-content: flex-start; - flex-direction: row-reverse; - - button { - margin-left: 10px; - @extend .btn; - - &.swal2-confirm { - @extend .btn, .primary; - font-weight: bold; - } - } - } - - .swal2-validation-message { - margin-top: 20px; - } -} diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 5b265278423..97d065c6893 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -5,7 +5,6 @@ import { NavigationEnd, Router } from "@angular/router"; import * as jq from "jquery"; import { IndividualConfig, ToastrService } from "ngx-toastr"; import { Subject, takeUntil } from "rxjs"; -import Swal from "sweetalert2"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; @@ -204,10 +203,6 @@ export class AppComponent implements OnDestroy, OnInit { for (const modal of modals) { (jq(modal) as any).modal("hide"); } - - if (document.querySelector(".swal-modal") != null) { - Swal.close(undefined); - } } }); @@ -258,7 +253,6 @@ export class AppComponent implements OnDestroy, OnInit { } await this.stateService.clean({ userId: userId }); - Swal.close(); if (redirect) { this.router.navigate(["/"]); } diff --git a/apps/web/src/scss/base.scss b/apps/web/src/scss/base.scss index ad02a7ce87d..4600620be87 100644 --- a/apps/web/src/scss/base.scss +++ b/apps/web/src/scss/base.scss @@ -173,9 +173,7 @@ code { } .btn:focus, -.swal2-popup .swal2-actions button:focus, .btn.focus, -.swal2-popup .swal2-actions button.focus, .form-control:focus { @include themify($themes) { box-shadow: 0 0 0 0.2rem themed("focus"); diff --git a/apps/web/src/scss/buttons.scss b/apps/web/src/scss/buttons.scss index 34a8ec66fa4..c803439fe4d 100644 --- a/apps/web/src/scss/buttons.scss +++ b/apps/web/src/scss/buttons.scss @@ -1,5 +1,4 @@ -.btn-primary, -.swal2-confirm { +.btn-primary { @include themify($themes) { background-color: themed("btnPrimary"); border-color: themed("btnPrimary"); @@ -37,8 +36,7 @@ } } -.btn-secondary, -.swal2-cancel { +.btn-secondary { @include themify($themes) { background-color: themed("btnSecondary"); border-color: themed("btnSecondaryBorder"); @@ -103,8 +101,7 @@ } } -.btn-danger, -.swal2-deny { +.btn-danger { @include themify($themes) { background-color: themed("btnDanger"); border-color: themed("btnDanger"); diff --git a/apps/web/src/scss/plugins.scss b/apps/web/src/scss/plugins.scss index 0610535eb55..8d3b685b031 100644 --- a/apps/web/src/scss/plugins.scss +++ b/apps/web/src/scss/plugins.scss @@ -130,96 +130,6 @@ } } -// SweetAlert2 - -[class*="swal2-"] { - &:not(.swal2-container, .swal2-confirm, .swal2-cancel, .swal2-deny) { - @include themify($themes) { - background-color: themed("backgroundColor"); - color: themed("textColor"); - } - } -} - -.swal2-container { - background-color: rgba(0, 0, 0, 0.3); -} - -.swal2-popup { - @include themify($themes) { - background-color: themed("backgroundColor"); - color: themed("textColor"); - } - border: $modal-content-border-width solid #9a9a9a; - @include border-radius($modal-content-border-radius); - padding: 15px 0 0; - width: 34em; // slightly bigger than the hardcoded 478px in v1. - - .swal2-header { - padding: 0 15px; - } - - .swal2-icon { - border: none; - height: auto; - margin: 0 auto; - width: auto; - } - - .swal2-content { - font-size: $font-size-base; - padding-bottom: 15px; - @include themify($themes) { - border-bottom: $modal-footer-border-width solid themed("separator"); - } - } - - i.swal-custom-icon { - display: block; - font-size: 35px; - margin: 0 auto; - } - - .swal2-title { - font-size: $font-size-lg; - margin: 0; - padding: 10px 0 15px; - @include themify($themes) { - color: themed("textHeadingColor"); - } - } - - .swal2-content { - font-size: $font-size-base; - padding: 0 15px 15px; - @include themify($themes) { - color: themed("textColor"); - } - } - - .swal2-actions { - @include border-radius($modal-content-border-radius); - display: flex; - flex-direction: row; - font-size: $font-size-base; - justify-content: flex-start; - margin: 0; - padding: 15px; - @include themify($themes) { - background-color: themed("backgroundColor"); - } - - button { - margin-right: 10px; - @extend .btn; - } - } - - .swal2-validation-message { - margin: 0 -15px; - } -} - date-input-polyfill { &[data-open="true"] { z-index: 10000 !important; diff --git a/apps/web/src/scss/styles.scss b/apps/web/src/scss/styles.scss index 0003f521c7b..2c0f9c21fd6 100644 --- a/apps/web/src/scss/styles.scss +++ b/apps/web/src/scss/styles.scss @@ -44,7 +44,6 @@ @import "~bootstrap/scss/_print"; @import "~ngx-toastr/toastr"; -@import "~#sweetalert2"; @import "./base"; @import "./buttons"; diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index bfdc8ae90ae..c7d2bcb5e01 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -349,10 +349,6 @@ const webpackConfig = { extensions: [".ts", ".js"], symlinks: false, modules: [path.resolve("../../node_modules")], - alias: { - sweetalert2: require.resolve("sweetalert2/dist/sweetalert2.js"), - "#sweetalert2": require.resolve("sweetalert2/src/sweetalert2.scss"), - }, fallback: { buffer: false, util: require.resolve("util/"), diff --git a/package-lock.json b/package-lock.json index 525fbb0a183..9b07e036e22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,7 +64,6 @@ "proper-lockfile": "4.1.2", "qrious": "4.0.2", "rxjs": "7.8.1", - "sweetalert2": "10.16.11", "tldts": "6.0.14", "utf-8-validate": "5.0.10", "zone.js": "0.12.0", @@ -37301,14 +37300,6 @@ "webpack": ">=2" } }, - "node_modules/sweetalert2": { - "version": "10.16.11", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-10.16.11.tgz", - "integrity": "sha512-Rdfabv2G89Tr8vmUTb1auWCYYesKBEWnkYPSi7XaiCIW0ZXXGK8Nw1wYKPEMLU6O8gMSMJe5m6MRKqMQsAQy9A==", - "funding": { - "url": "https://sweetalert2.github.io/#donations" - } - }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index d0673441246..6f25336db04 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,6 @@ "proper-lockfile": "4.1.2", "qrious": "4.0.2", "rxjs": "7.8.1", - "sweetalert2": "10.16.11", "tldts": "6.0.14", "utf-8-validate": "5.0.10", "zone.js": "0.12.0", From 0a004d695a68b2e29dc3318956eec79e5f666b26 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Tue, 12 Sep 2023 10:10:08 -0300 Subject: [PATCH 122/135] [PM-3881] Fix ENV=cloud npm run build:oss:watch (#6262) --- apps/web/config/cloud.json | 3 ++- apps/web/config/qa.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index 5459ce4057b..45269d18c63 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -13,7 +13,8 @@ "dev": { "proxyApi": "https://api.bitwarden.com", "proxyIdentity": "https://identity.bitwarden.com", - "proxyEvents": "https://events.bitwarden.com" + "proxyEvents": "https://events.bitwarden.com", + "proxyNotifications": "https://notifications.bitwarden.com" }, "flags": { "secretsManager": true, diff --git a/apps/web/config/qa.json b/apps/web/config/qa.json index 3632c696856..6f514079010 100644 --- a/apps/web/config/qa.json +++ b/apps/web/config/qa.json @@ -7,7 +7,8 @@ "dev": { "proxyApi": "https://api.qa.bitwarden.pw", "proxyIdentity": "https://identity.qa.bitwarden.pw", - "proxyEvents": "https://events.qa.bitwarden.pw" + "proxyEvents": "https://events.qa.bitwarden.pw", + "proxyNotifications": "https://notifications.qa.bitwarden.pw" }, "flags": { "secretsManager": true, From 81ab34382868d3dca79493bdf5c0d3481e834c39 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:21:02 -0400 Subject: [PATCH 123/135] DEVOPS-1554 - Fix browser artifacts (#6224) --- .github/workflows/build-browser.yml | 49 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 05b89d66c33..b1242ee23ed 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -114,9 +114,6 @@ jobs: - locales-test env: _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} - defaults: - run: - working-directory: apps/browser steps: - name: Checkout repo uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 @@ -139,19 +136,6 @@ jobs: npm --version node-gyp --version - - name: NPM setup - run: npm ci - working-directory: ./ - - - name: Build - run: npm run dist - - # - name: Build Manifest v3 - # run: npm run dist:mv3 - - - name: Gulp - run: gulp ci - - name: Build sources for reviewers run: | # Include hidden files in glob copy @@ -174,55 +158,70 @@ jobs: cp -r libs/* browser-source/libs zip -r browser-source.zip browser-source - working-directory: ./ + + - name: NPM setup + run: npm ci + working-directory: browser-source/ + + - name: Build + run: npm run dist + working-directory: browser-source/apps/browser + + # - name: Build Manifest v3 + # run: npm run dist:mv3 + # working-directory: browser-source/apps/browser + + - name: Gulp + run: gulp ci + working-directory: browser-source/apps/browser - name: Upload Opera artifact uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-opera-${{ env._BUILD_NUMBER }}.zip - path: apps/browser/dist/dist-opera.zip + path: browser-source/apps/browser/dist/dist-opera.zip if-no-files-found: error # - name: Upload Opera MV3 artifact # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 # with: # name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip - # path: apps/browser/dist/dist-opera-mv3.zip + # path: browser-source/apps/browser/dist/dist-opera-mv3.zip # if-no-files-found: error - name: Upload Chrome artifact uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-chrome-${{ env._BUILD_NUMBER }}.zip - path: apps/browser/dist/dist-chrome.zip + path: browser-source/apps/browser/dist/dist-chrome.zip if-no-files-found: error # - name: Upload Chrome MV3 artifact # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 # with: # name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip - # path: apps/browser/dist/dist-chrome-mv3.zip + # path: browser-source/apps/browser/dist/dist-chrome-mv3.zip # if-no-files-found: error - name: Upload Firefox artifact uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-firefox-${{ env._BUILD_NUMBER }}.zip - path: apps/browser/dist/dist-firefox.zip + path: browser-source/apps/browser/dist/dist-firefox.zip if-no-files-found: error - name: Upload Edge artifact uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-edge-${{ env._BUILD_NUMBER }}.zip - path: apps/browser/dist/dist-edge.zip + path: browser-source/apps/browser/dist/dist-edge.zip if-no-files-found: error # - name: Upload Edge MV3 artifact # uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 # with: # name: dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip - # path: apps/browser/dist/dist-edge-mv3.zip + # path: browser-source/apps/browser/dist/dist-edge-mv3.zip # if-no-files-found: error - name: Upload browser source @@ -237,7 +236,7 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: coverage-${{ env._BUILD_NUMBER }}.zip - path: apps/browser/coverage/coverage-${{ env._BUILD_NUMBER }}.zip + path: browser-source/apps/browser/coverage/coverage-${{ env._BUILD_NUMBER }}.zip if-no-files-found: error build-safari: From a6e4ad4e7ee83abf83257256413c61cd8964d2d2 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 14 Sep 2023 11:50:06 +0200 Subject: [PATCH 124/135] [PM-3818] Remove unused css (#6218) --- apps/web/src/scss/forms.scss | 4 --- apps/web/src/scss/modals.scss | 21 --------------- apps/web/src/scss/pages.scss | 51 ----------------------------------- 3 files changed, 76 deletions(-) diff --git a/apps/web/src/scss/forms.scss b/apps/web/src/scss/forms.scss index acba154f30f..9404bc94031 100644 --- a/apps/web/src/scss/forms.scss +++ b/apps/web/src/scss/forms.scss @@ -224,10 +224,6 @@ input[type="checkbox"] { } } -.error-summary { - margin-top: 1rem; -} - .error-inline { @include themify($themes) { color: themed("danger"); diff --git a/apps/web/src/scss/modals.scss b/apps/web/src/scss/modals.scss index 29e7191cd72..92e6ef3a18e 100644 --- a/apps/web/src/scss/modals.scss +++ b/apps/web/src/scss/modals.scss @@ -6,21 +6,6 @@ } } -.modal-footer-content { - border: none; - border-radius: none; - @include themify($themes) { - background-color: themed("footerBackgroundColor"); - } - position: relative; - display: flex; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-clip: padding-box; - outline: 0; -} - .modal-dialog { border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 0.3rem; @@ -100,12 +85,6 @@ } } -#totpImage { - @include themify($themes) { - filter: themed("imgFilter"); - } -} - .totp { .totp-code { @extend .text-monospace; diff --git a/apps/web/src/scss/pages.scss b/apps/web/src/scss/pages.scss index 7b56a1f9b5b..4dbee2ac507 100644 --- a/apps/web/src/scss/pages.scss +++ b/apps/web/src/scss/pages.scss @@ -149,54 +149,3 @@ app-sponsored-families { } } } - -.collapsable-row { - display: flex; - padding-top: 15px; - i { - margin-top: 3px; - } - .filter-title { - padding-left: 5px; - } - &.active { - @include themify($themes) { - color: themed("primary"); - } - } -} - -.vault-filter-option { - padding-bottom: 3px; - &.active { - @include themify($themes) { - color: themed("primary"); - font-weight: bold; - } - } - button.filter-options-icon { - background: none; - border: none; - padding: 0; - - &:hover, - &:focus { - @include themify($themes) { - color: themed("iconHover") !important; - box-shadow: none; - } - } - } -} - -.org-filter-heading { - @include themify($themes) { - color: themed("textColor"); - } - &.active { - @include themify($themes) { - color: themed("primary"); - font-weight: bold; - } - } -} From 931a2258e29fda19b281e417b997dbc31fe3be32 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:29:41 +1000 Subject: [PATCH 125/135] [PM-3883] Fix ConfigService.serverConfig$ initial values and error handling (#6272) * Always fetch ServerConfig from server, use stored value as fallback * Handle errors in server fetch --- .../browser/src/background/main.background.ts | 1 + .../services/browser-config.service.ts | 4 +- .../src/popup/services/services.module.ts | 6 ++- .../src/services/jslib-services.module.ts | 1 + .../services/config/config.service.spec.ts | 48 +++++++++++++++---- .../services/config/config.service.ts | 28 +++++------ 6 files changed, 62 insertions(+), 26 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index e646b98d414..3aba0c77679 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -540,6 +540,7 @@ export default class MainBackground { this.configApiService, this.authService, this.environmentService, + this.logService, true ); this.browserPopoutWindowService = new BrowserPopoutWindowService(); diff --git a/apps/browser/src/platform/services/browser-config.service.ts b/apps/browser/src/platform/services/browser-config.service.ts index f928fdd0726..39d1fc565eb 100644 --- a/apps/browser/src/platform/services/browser-config.service.ts +++ b/apps/browser/src/platform/services/browser-config.service.ts @@ -4,6 +4,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; @@ -19,8 +20,9 @@ export class BrowserConfigService extends ConfigService { configApiService: ConfigApiServiceAbstraction, authService: AuthService, environmentService: EnvironmentService, + logService: LogService, subscribe = false ) { - super(stateService, configApiService, authService, environmentService, subscribe); + super(stateService, configApiService, authService, environmentService, logService, subscribe); } } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 4e4f914f230..0ba396af92f 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -43,7 +43,10 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { FileUploadService } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; +import { + LogService, + LogService as LogServiceAbstraction, +} from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { @@ -502,6 +505,7 @@ function getBgService(service: keyof MainBackground) { ConfigApiServiceAbstraction, AuthServiceAbstraction, EnvironmentService, + LogService, ], }, ], diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 5d279657101..02f6278fec7 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -647,6 +647,7 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; ConfigApiServiceAbstraction, AuthServiceAbstraction, EnvironmentServiceAbstraction, + LogService, ], }, { diff --git a/libs/common/src/platform/services/config/config.service.spec.ts b/libs/common/src/platform/services/config/config.service.spec.ts index 511ecfd5c86..2f3a90f70fd 100644 --- a/libs/common/src/platform/services/config/config.service.spec.ts +++ b/libs/common/src/platform/services/config/config.service.spec.ts @@ -6,6 +6,7 @@ import { AuthenticationStatus } from "../../../auth/enums/authentication-status" import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-api.service.abstraction"; import { ServerConfig } from "../../abstractions/config/server-config"; import { EnvironmentService } from "../../abstractions/environment.service"; +import { LogService } from "../../abstractions/log.service"; import { StateService } from "../../abstractions/state.service"; import { ServerConfigData } from "../../models/data/server-config.data"; import { @@ -21,6 +22,7 @@ describe("ConfigService", () => { let configApiService: MockProxy; let authService: MockProxy; let environmentService: MockProxy; + let logService: MockProxy; let serverResponseCount: number; // increments to track distinct responses received from server @@ -31,7 +33,8 @@ describe("ConfigService", () => { stateService, configApiService, authService, - environmentService + environmentService, + logService ); configService.init(); return configService; @@ -42,6 +45,8 @@ describe("ConfigService", () => { configApiService = mock(); authService = mock(); environmentService = mock(); + logService = mock(); + environmentService.urls = new ReplaySubject(1); serverResponseCount = 1; @@ -56,10 +61,12 @@ describe("ConfigService", () => { jest.useRealTimers(); }); - it("Loads config from storage", (done) => { + it("Uses storage as fallback", (done) => { const storedConfigData = serverConfigDataFactory("storedConfig"); stateService.getServerConfig.mockResolvedValueOnce(storedConfigData); + configApiService.get.mockRejectedValueOnce(new Error("Unable to fetch")); + const configService = configServiceFactory(); configService.serverConfig$.pipe(take(1)).subscribe((config) => { @@ -68,6 +75,30 @@ describe("ConfigService", () => { expect(stateService.setServerConfig).not.toHaveBeenCalled(); done(); }); + + configService.triggerServerConfigFetch(); + }); + + it("Stream does not error out if fetch fails", (done) => { + const storedConfigData = serverConfigDataFactory("storedConfig"); + stateService.getServerConfig.mockResolvedValueOnce(storedConfigData); + + const configService = configServiceFactory(); + + configService.serverConfig$.pipe(skip(1), take(1)).subscribe((config) => { + try { + expect(config.gitHash).toEqual("server1"); + done(); + } catch (e) { + done(e); + } + }); + + configApiService.get.mockRejectedValueOnce(new Error("Unable to fetch")); + configService.triggerServerConfigFetch(); + + configApiService.get.mockResolvedValueOnce(serverConfigResponseFactory("server1")); + configService.triggerServerConfigFetch(); }); describe("Fetches config from server", () => { @@ -80,8 +111,8 @@ describe("ConfigService", () => { (hours: number, done: jest.DoneCallback) => { const configService = configServiceFactory(); - // skip initial load from storage, plus previous hours (if any) - configService.serverConfig$.pipe(skip(hours), take(1)).subscribe((config) => { + // skip previous hours (if any) + configService.serverConfig$.pipe(skip(hours - 1), take(1)).subscribe((config) => { try { expect(config.gitHash).toEqual("server" + hours); expect(configApiService.get).toHaveBeenCalledTimes(hours); @@ -99,8 +130,7 @@ describe("ConfigService", () => { it("when environment URLs change", (done) => { const configService = configServiceFactory(); - // skip initial load from storage - configService.serverConfig$.pipe(skip(1), take(1)).subscribe((config) => { + configService.serverConfig$.pipe(take(1)).subscribe((config) => { try { expect(config.gitHash).toEqual("server1"); done(); @@ -115,8 +145,7 @@ describe("ConfigService", () => { it("when triggerServerConfigFetch() is called", (done) => { const configService = configServiceFactory(); - // skip initial load from storage - configService.serverConfig$.pipe(skip(1), take(1)).subscribe((config) => { + configService.serverConfig$.pipe(take(1)).subscribe((config) => { try { expect(config.gitHash).toEqual("server1"); done(); @@ -134,8 +163,7 @@ describe("ConfigService", () => { authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked); const configService = configServiceFactory(); - // skip initial load from storage - configService.serverConfig$.pipe(skip(1), take(1)).subscribe(() => { + configService.serverConfig$.pipe(take(1)).subscribe(() => { try { expect(stateService.setServerConfig).toHaveBeenCalledWith( expect.objectContaining({ gitHash: "server1" }) diff --git a/libs/common/src/platform/services/config/config.service.ts b/libs/common/src/platform/services/config/config.service.ts index c3a5fab4d70..008f5a764d3 100644 --- a/libs/common/src/platform/services/config/config.service.ts +++ b/libs/common/src/platform/services/config/config.service.ts @@ -1,11 +1,11 @@ import { ReplaySubject, Subject, + catchError, concatMap, + defer, delayWhen, - filter, firstValueFrom, - from, map, merge, timer, @@ -18,6 +18,7 @@ import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-ap import { ConfigServiceAbstraction } from "../../abstractions/config/config.service.abstraction"; import { ServerConfig } from "../../abstractions/config/server-config"; import { EnvironmentService, Region } from "../../abstractions/environment.service"; +import { LogService } from "../../abstractions/log.service"; import { StateService } from "../../abstractions/state.service"; import { ServerConfigData } from "../../models/data/server-config.data"; @@ -38,6 +39,7 @@ export class ConfigService implements ConfigServiceAbstraction { private configApiService: ConfigApiServiceAbstraction, private authService: AuthService, private environmentService: EnvironmentService, + private logService: LogService, // Used to avoid duplicate subscriptions, e.g. in browser between the background and popup private subscribe = true @@ -48,14 +50,16 @@ export class ConfigService implements ConfigServiceAbstraction { return; } - // Get config from storage on initial load - const fromStorage = from(this.stateService.getServerConfig()).pipe( - map((data) => (data == null ? null : new ServerConfig(data))) + const latestServerConfig$ = defer(() => this.configApiService.get()).pipe( + map((response) => new ServerConfigData(response)), + delayWhen((data) => this.saveConfig(data)), + catchError((e: unknown) => { + // fall back to stored ServerConfig (if any) + this.logService.error("Unable to fetch ServerConfig: " + (e as Error)?.message); + return this.stateService.getServerConfig(); + }) ); - fromStorage.subscribe((config) => this._serverConfig.next(config)); - - // Fetch config from server // If you need to fetch a new config when an event occurs, add an observable that emits on that event here merge( timer(ONE_HOUR_IN_MILLISECONDS, ONE_HOUR_IN_MILLISECONDS), // after 1 hour, then every hour @@ -63,12 +67,8 @@ export class ConfigService implements ConfigServiceAbstraction { this._forceFetchConfig // manual ) .pipe( - delayWhen(() => fromStorage), // wait until storage has emitted first to avoid a race condition - concatMap(() => this.configApiService.get()), - filter((response) => response != null), - map((response) => new ServerConfigData(response)), - delayWhen((data) => this.saveConfig(data)), - map((data) => new ServerConfig(data)) + concatMap(() => latestServerConfig$), + map((data) => (data == null ? null : new ServerConfig(data))) ) .subscribe((config) => this._serverConfig.next(config)); From c9245df8d30270df52fbf98bd4d9179bda1cdb95 Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:41:15 +0100 Subject: [PATCH 126/135] [PM-3878] Setting send password to null if it is empty (#6276) * setting send password to null if it is empty * Using Utils.IsNullOrWhiteSpace to verify name and password on SendAddEdit. Removed unnecessary setting password as null on desktop --- .../src/app/tools/send/add-edit.component.ts | 1 - .../src/tools/send/add-edit.component.ts | 20 ++++++------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/desktop/src/app/tools/send/add-edit.component.ts b/apps/desktop/src/app/tools/send/add-edit.component.ts index 98764866a54..aeb8f951175 100644 --- a/apps/desktop/src/app/tools/send/add-edit.component.ts +++ b/apps/desktop/src/app/tools/send/add-edit.component.ts @@ -50,7 +50,6 @@ export class AddEditComponent extends BaseAddEditComponent { } async refresh() { - this.password = null; const send = await this.loadSend(); this.send = await send.decrypt(); this.updateFormValues(); diff --git a/libs/angular/src/tools/send/add-edit.component.ts b/libs/angular/src/tools/send/add-edit.component.ts index d77da9c42c8..0827ad318cc 100644 --- a/libs/angular/src/tools/send/add-edit.component.ts +++ b/libs/angular/src/tools/send/add-edit.component.ts @@ -11,6 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { Send } from "@bitwarden/common/tools/send/models/domain/send"; @@ -67,7 +68,6 @@ export class AddEditComponent implements OnInit, OnDestroy { disableHideEmail = false; send: SendView; hasPassword: boolean; - password: string; showPassword = false; formPromise: Promise; deletePromise: Promise; @@ -252,7 +252,7 @@ export class AddEditComponent implements OnInit, OnDestroy { this.send.disabled = this.formGroup.controls.disabled.value; this.send.type = this.type; - if (this.send.name == null || this.send.name === "") { + if (Utils.isNullOrWhitespace(this.send.name)) { this.platformUtilsService.showToast( "error", this.i18nService.t("errorOccurred"), @@ -286,11 +286,8 @@ export class AddEditComponent implements OnInit, OnDestroy { } } - if ( - this.formGroup.controls.password.value != null && - this.formGroup.controls.password.value.trim() === "" - ) { - this.password = null; + if (Utils.isNullOrWhitespace(this.send.password)) { + this.send.password = null; } this.formPromise = this.encryptSend(file).then(async (encSend) => { @@ -379,12 +376,7 @@ export class AddEditComponent implements OnInit, OnDestroy { } protected async encryptSend(file: File): Promise<[Send, EncArrayBuffer]> { - const sendData = await this.sendService.encrypt( - this.send, - file, - this.formGroup.controls.password.value, - null - ); + const sendData = await this.sendService.encrypt(this.send, file, this.send.password, null); // Parse dates try { @@ -420,7 +412,7 @@ export class AddEditComponent implements OnInit, OnDestroy { hideEmail: this.send?.hideEmail ?? false, disabled: this.send?.disabled ?? false, type: this.send.type ?? this.type, - password: "", + password: null, selectedDeletionDatePreset: this.editMode ? DatePreset.Custom : DatePreset.SevenDays, selectedExpirationDatePreset: this.editMode ? DatePreset.Custom : DatePreset.Never, From 86c5bd111cd803650f9ff1cc7c88de412ea1cbb9 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 14 Sep 2023 14:28:41 +0200 Subject: [PATCH 127/135] Add a warning to not modify this file (#6235) --- apps/web/src/app/app.module.ts | 8 ++++++-- bitwarden_license/bit-web/src/app/app.module.ts | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/app.module.ts b/apps/web/src/app/app.module.ts index eca957a070f..2a67232e3db 100644 --- a/apps/web/src/app/app.module.ts +++ b/apps/web/src/app/app.module.ts @@ -9,9 +9,14 @@ import { AppComponent } from "./app.component"; import { CoreModule } from "./core"; import { OssRoutingModule } from "./oss-routing.module"; import { OssModule } from "./oss.module"; -import { SendComponent } from "./tools/send/send.component"; import { WildcardRoutingModule } from "./wildcard-routing.module"; +/** + * This is the AppModule for the OSS version of Bitwarden. + * `bitwarden_license/bit-web/app.module.ts` contains the commercial version. + * + * You probably do not want to modify this file. Consider editing `oss.module.ts` instead. + */ @NgModule({ imports: [ OssModule, @@ -22,7 +27,6 @@ import { WildcardRoutingModule } from "./wildcard-routing.module"; DragDropModule, LayoutModule, OssRoutingModule, - SendComponent, WildcardRoutingModule, // Needs to be last to catch all non-existing routes ], declarations: [AppComponent], diff --git a/bitwarden_license/bit-web/src/app/app.module.ts b/bitwarden_license/bit-web/src/app/app.module.ts index c5de6f6d5fa..77075a20a44 100644 --- a/bitwarden_license/bit-web/src/app/app.module.ts +++ b/bitwarden_license/bit-web/src/app/app.module.ts @@ -19,6 +19,12 @@ import { MaximumVaultTimeoutPolicyComponent } from "./admin-console/policies/max import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; +/** + * This is the AppModule for the commercial version of Bitwarden. + * `apps/web/app.module.ts` contains the OSS version. + * + * You probably do not want to modify this file. Consider editing `oss.module.ts` instead. + */ @NgModule({ imports: [ OverlayModule, From 1a5d95caf86a15a9d09c8d5d2f61c19652a98b76 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:25:18 +0200 Subject: [PATCH 128/135] Update message key in browser and desktop (#6264) Co-authored-by: Daniel James Smith --- apps/browser/src/_locales/en/messages.json | 4 ++-- apps/desktop/src/locales/en/messages.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index b032c6d2de3..15606aa220a 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", From 5f7eb9dc404823b63ca74d5512f2fb91ebc24192 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:12:22 +0000 Subject: [PATCH 129/135] Autosync the updated translations (#6291) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/az/messages.json | 4 +- apps/web/src/locales/bg/messages.json | 10 +- apps/web/src/locales/ca/messages.json | 4 +- apps/web/src/locales/fi/messages.json | 6 +- apps/web/src/locales/hr/messages.json | 30 +- apps/web/src/locales/it/messages.json | 4 +- apps/web/src/locales/ja/messages.json | 2 +- apps/web/src/locales/lv/messages.json | 48 +-- apps/web/src/locales/pt_BR/messages.json | 4 +- apps/web/src/locales/pt_PT/messages.json | 2 +- apps/web/src/locales/tr/messages.json | 6 +- apps/web/src/locales/zh_CN/messages.json | 70 ++-- apps/web/src/locales/zh_TW/messages.json | 416 +++++++++++------------ 13 files changed, 303 insertions(+), 303 deletions(-) diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 18eea5284fe..5e601873e7d 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -1426,10 +1426,10 @@ "message": "Giriş edərkən əlavə bir addım tələb edərək hesabınızı qoruyun." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Təşkilatınız üçün iki addımlı girişi fəallaşdır." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Bitwarden iki addımlı Giriş seçimlərini aşağıdakını istifadə edərək üzvlər üçün tətbiq et ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 4cf667a626c..1813900cd1b 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -1426,14 +1426,14 @@ "message": "Допълнителна защита на абонамента чрез изискването на допълнително действие при вписване." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Включете двустепенното удостоверяване за организацията си." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Задължете членовете да използват вариантите за двустепенно удостоверяване в Битуорден, като използвате ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { - "message": "Two-step Login Policy" + "message": "Политика за двустепенно удостоверяване" }, "twoStepLoginOrganizationDuoDesc": { "message": "За да направите двустепенното удостоверяване чрез Duo задължително, използвайте настройките по-долу." @@ -1723,7 +1723,7 @@ "message": "Открити са записи без двустепенна идентификация" }, "inactive2faFoundDesc": { - "message": "We found $COUNT$ website(s) in your vault that may not be configured with two-step login (according to 2fa.directory). To further protect these accounts, you should set up two-step login.", + "message": "В трезора ви има $COUNT$ уебсайт(а), които е възможно да не са настроени за вписване чрез двустепенно удостоверяване (според сайта 2fa.directory). За да защитите тези регистрации, трябва да настроите двустепенното удостоверяване.", "placeholders": { "count": { "content": "$1", @@ -6639,7 +6639,7 @@ "message": "Променете настройките си за шифроване, така че да отговарят на новите препоръки за сигурността и да подобрите защитата на регистрацията си." }, "changeKdfLoggedOutWarning": { - "message": "Proceeding will log you out of all active sessions. You will need to log back in and complete two-step login setup. We recommend exporting your vault before changing your encryption settings to prevent data loss." + "message": "Ако продължите, ще излезете от всички активни сесии. Ще трябва да се впишете отново и да завършите настройката на двустепенното удостоверяване. Препоръчително е да изнесете трезора си преди да променяте настройките за шифроване, за да избегнете загуба на данни." }, "secretsManager": { "message": "Управление на тайни" diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index d3283662601..db32f7ca8d8 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -1426,10 +1426,10 @@ "message": "Protegiu el vostre compte exigint un pas addicional en iniciar sessió." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Activeu l'inici de sessió de doble factor per a la vostra organització." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Apliqueu les opcions d'inici de sessió de dobble factor de Bitwarden per als membres mitjançant la ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 296d49f7133..82c7fbea930 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -1426,10 +1426,10 @@ "message": "Suojaa tilisi vaatimalla sisäänkirjautumiseen toinen todennusvaihe." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Ota kaksivaiheinen kirjautuminen käyttöön organisaatiollesi." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Pakota jäsenille Bitwardenin kaksivaiheisen kirjautumisen valinnat käytännöllä: ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -7157,7 +7157,7 @@ "message": "Palvelutilin mahdollinen enimmäiskustannus" }, "loggedInExclamation": { - "message": "Kirjautuminen onnistui!" + "message": "Kirjautuminen onnistui." }, "smBetaEndedDesc": { "message": "Salaisuushallinnan beta päättyi $BETA_ENDING_DATE$. Sinulla on $DAYS$ päivää aikaa lisätä Salaisuushallinta maksulliseen tilaukseesi säilyttääksesi Salaisuushallinnassa olevien tietojesi käyttöoikeuden. Lisää Salaisuushallinta tilaukseesi olemalla yhteydessä asiakaspalveluun.", diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 83621989bf8..27f841761d7 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -609,7 +609,7 @@ "message": "Prijava uređajem" }, "loginWithDeviceEnabledNote": { - "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" + "message": "Prijava uređajem mora biti namještena u postavka Bitwarden mobilne aplikacije. Trebaš drugu opciju?" }, "loginWithMasterPassword": { "message": "Prijava glavnom lozinkom" @@ -712,7 +712,7 @@ "message": "Došlo je do neočekivane pogreške." }, "expirationDateError": { - "message": "Please select an expiration date that is in the future." + "message": "Odaberi datum isteka u budućnosti." }, "emailAddress": { "message": "Adresa e-pošte" @@ -955,7 +955,7 @@ "message": "Kopiraj kôd za provjeru" }, "copyUuid": { - "message": "Copy UUID" + "message": "Kopiraj UUID" }, "warning": { "message": "Upozorenje" @@ -1300,10 +1300,10 @@ "message": "Learn about your import options" }, "selectImportFolder": { - "message": "Select a folder" + "message": "Odaberi mapu" }, "selectImportCollection": { - "message": "Select a collection" + "message": "Odaberi zbirku" }, "importTargetHint": { "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", @@ -2068,7 +2068,7 @@ "message": "Otkaži pretplatu" }, "subscriptionExpiration": { - "message": "Subscription expiration" + "message": "Istek pretplate" }, "subscriptionCanceled": { "message": "Pretplata otkazana." @@ -2676,7 +2676,7 @@ "message": "Web trezor" }, "bitWebVault": { - "message": "Bitwarden Web vault" + "message": "Bitwarden Web trezor" }, "bitSecretsManager": { "message": "Bitwarden Secrets Manager" @@ -2703,7 +2703,7 @@ "message": "Prijava neuspješna zbog pogrešne dvostruke autentifikacije." }, "incorrectPassword": { - "message": "Incorrect password" + "message": "Netočna lozinka" }, "incorrectCode": { "message": "Incorrect code" @@ -2802,7 +2802,7 @@ } }, "viewCollectionWithName": { - "message": "View collection - $NAME$", + "message": "Prikaži zbirku - $NAME$", "placeholders": { "name": { "content": "$1", @@ -2811,7 +2811,7 @@ } }, "editItemWithName": { - "message": "Edit item - $NAME$", + "message": "Uredi stavku - $NAME$", "placeholders": { "name": { "content": "$1", @@ -3793,10 +3793,10 @@ "message": "Nakon isteka trezora" }, "vaultTimeoutActionLockDesc": { - "message": "Potrebno je ponovno unijeti glavnu lozinku ili na drugi način otključati za pristup tvom trezoru." + "message": "Za pristup tvom trezoru, potrebno je ponovno unijeti glavnu lozinku ili ga na drugi način otključati." }, "vaultTimeoutActionLogOutDesc": { - "message": "Potrebno je ponovno unijeti korisničko ime i glavnu lozinku za pristup tvom trezoru." + "message": "Za pristup tvom trezoru, potrebno je ponovno unijeti korisničko ime i glavnu lozinku." }, "lock": { "message": "Zaključaj", @@ -3870,7 +3870,7 @@ "message": "Odjava će ukloniti pristup tvojem trezoru i zahtijeva mrežnu potvrdu identiteta nakon isteka vremenske neaktivnosti. Sigurno želiš koristiti ovu postavku?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Potvrda akcije vremenske neaktivnosti" + "message": "Potvrda radnje nakon vremenske neaktivnosti" }, "hidePasswords": { "message": "Sakrij lozinke" @@ -4901,7 +4901,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "Pravilo tvoje organizacije utječe na istek trezora. Najveće dozvoljeno vrijeme isteka je $HOURS$:$MINUTES$ h. Tvoja radnja nakon isteka trezora je: $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -4918,7 +4918,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "Pravilo tvoje organizacije podesilo je radnju nakon isteka trezora na: $ACTION$.", "placeholders": { "action": { "content": "$1", diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 35f6b4d6a3c..acdfd13370e 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -1426,10 +1426,10 @@ "message": "Proteggi il tuo account richiedendo un passaggio aggiuntivo all'accesso." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Abilita la verifica in due passaggi per la tua organizzazione." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Forza le opzioni di verifica in due passaggi di Bitwarden per i membri usando la ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 45a9b4db63b..9d2a222220d 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -1426,7 +1426,7 @@ "message": "ログイン時に追加の手順を必要としアカウントを保護します。" }, "twoStepLoginTeamsDesc": { - "message": "組織の2段階認証を有効にする。" + "message": "組織の2段階認証を有効化します。" }, "twoStepLoginEnterpriseDescStart": { "message": "Bitwardenの2段階認証をメンバーに強制するには、次のようにします。 ", diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 3dce02f1ec4..47f2bf8d589 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -3,7 +3,7 @@ "message": "Kāda veida vienums tas ir?" }, "name": { - "message": "Nosaukums" + "message": "Vārds" }, "uri": { "message": "URI" @@ -415,7 +415,7 @@ "message": "Pārvietot uz apvienību" }, "valueCopied": { - "message": "$VALUE$ ievietota starpliktuvē", + "message": "$VALUE$ ir starpliktuvē", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -830,7 +830,7 @@ "message": "Šim kontam ir iespējota divpakāpju pieteikšanās, bet šajā pārlūkā netiek atbalstīts neviens no uzstādītajiem divpakāpju pārbaudes nodrošinātājiem." }, "noTwoStepProviders2": { - "message": "Lūgums izmantot atbalstītu tīmekļa pārlūku (piemēram Chrome) un/vai pievienot papildus nodrošinātājus, kas tiek labāk atbalstīti dažādos pārlūkos (piemēram autentificētāja lietotni)." + "message": "Lūgums izmantot atbalstītu tīmekļa pārlūku (piemēram Chrome) un/vai pievienot papildu nodrošinātājus, kas tiek labāk atbalstīti dažādos pārlūkos (piemēram autentificētāja lietotni)." }, "twoStepOptions": { "message": "Divpakāpju pieteikšanās iespējas" @@ -852,10 +852,10 @@ "message": "YubiKey OTP drošības atslēga" }, "yubiKeyDesc": { - "message": "Izmantojiet YubiKey, lai piekļūtu savam kontam. Darbojas ar YubiKey 4. sēriju, 5. sēriju un NEO ierīcēm." + "message": "YubiKey ir izmantojams, lai piekļūtu savam kontam. Darbojas ar YubiKey 4. un 5. sēriju un NEO ierīcēm." }, "duoDesc": { - "message": "Apstiprini ar Duo Security, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu!", + "message": "Ar Duo Security apliecināšanu var veikt ar Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { @@ -863,7 +863,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "u2fDesc": { - "message": "Lai piekļūtu savam kontam, izmantojiet jebkuru ar FIDO U2F saderīgu drošības atslēgu." + "message": "Ir izmantojama jebkura ar FIDO U2F saderīga drošības atslēga, lai piekļūtu savam kontam." }, "u2fTitle": { "message": "FIDO U2F drošības atslēga" @@ -872,7 +872,7 @@ "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Izmantot jebkuru WebAuthn atbalstošu drošības atslēgu, lai piekļūtu kontam." + "message": "Ir izmantojama jebkura WebAuthn atbalstošu drošības atslēgu, lai piekļūtu kontam." }, "webAuthnMigrated": { "message": "(Pārgājis no FIDO)" @@ -1231,19 +1231,19 @@ "message": "Glabātava tika iztīrīta." }, "deleteAccount": { - "message": "Dzēst kontu" + "message": "Izdzēst kontu" }, "deleteAccountDesc": { "message": "Turpināt zemāk, lai izdzēstu kontu un visus saistītos datus." }, "deleteAccountWarning": { - "message": "Konta dzēšana ir paliekoša. To nevar atsaukt." + "message": "Konta izdzēšana ir neatgriezeniska. To nevar atsaukt." }, "accountDeleted": { "message": "Konts izdzēsts" }, "accountDeletedDesc": { - "message": "Konts ir slēgts, un visi saistītie dati ir izdzēsti." + "message": "Konts tika slēgts, un visi saistītie dati tika izdzēsti." }, "myAccount": { "message": "Mans konts" @@ -1516,7 +1516,7 @@ "message": "Šīs ir ieteicamās lietotnes, bet darbosies arī citas autentificētāja lietotnes." }, "twoStepAuthenticatorScanCode": { - "message": "Nolasīt šo kvadrātkodu ar autentificētāja lietotni" + "message": "Šis kvadrātkods ir jānolasa ar autentificētāja lietotni" }, "key": { "message": "Atslēga" @@ -1919,10 +1919,10 @@ "description": "Another way of saying \"Get a Premium membership\"" }, "premiumUpdated": { - "message": "Tu esi pārgājis uz Premium." + "message": "Konts tika uzlabots uz Premium." }, "premiumUpgradeUnlockFeatures": { - "message": "Jauniniet savu kontu uz Premium abonementu un atbloķējiet dažas lieliskas papildu funkcijas." + "message": "Uzlabo savu kontu ar Premium dalību un atslēdz dažas lieliskas papildu iespējas!" }, "premiumSignUpStorage": { "message": "1 GB šifrētas krātuves datņu pielikumiem." @@ -1990,13 +1990,13 @@ } }, "additionalStorageGb": { - "message": "Papildus krātuve (GB)" + "message": "Papildu krātuve (GB)" }, "additionalStorageGbDesc": { - "message": "# papildus GB" + "message": "# papildu GB" }, "additionalStorageIntervalDesc": { - "message": "Pašreizējais plāns iever $SIZE$ šifrētas datņu krātuves. Ir iespējams pievienot papildus krātuvi par $PRICE$ GB / $INTERVAL$.", + "message": "Pašreizējais plāns iever $SIZE$ šifrētas datņu krātuves. Ir iespējams pievienot papildu krātuvi par $PRICE$ GB / $INTERVAL$.", "placeholders": { "size": { "content": "$1", @@ -2269,13 +2269,13 @@ "message": "Lietotāju vietas" }, "additionalUserSeats": { - "message": "Papildus lietotāju vietas" + "message": "Papildu lietotāju vietas" }, "userSeatsDesc": { "message": "# lietotāju vietas" }, "userSeatsAdditionalDesc": { - "message": "Pašreizējā plānā ir iekļauta(s) $BASE_SEATS$ lietotāju vieta(s). Papildus lietotājus var pievienot par $SEAT_PRICE$ mēnesī par katru.", + "message": "Pašreizējā plānā ir iekļauta(s) $BASE_SEATS$ lietotāju vieta(s). Papildu lietotājus var pievienot par $SEAT_PRICE$ mēnesī par katru.", "placeholders": { "base_seats": { "content": "$1", @@ -2288,7 +2288,7 @@ } }, "userSeatsHowManyDesc": { - "message": "Cik daudz lietotāju vietas ir nepieciešamas? Vēlāk ir iespējams pievienot papildus vietas, ja nepieciešams." + "message": "Cik daudz lietotāju vietas ir nepieciešamas? Vēlāk ir iespējams pievienot papildu vietas, ja nepieciešams." }, "planNameFree": { "message": "Bezmaksas", @@ -2334,7 +2334,7 @@ } }, "additionalUsers": { - "message": "Papildus lietotāji" + "message": "Papildu lietotāji" }, "costPerUser": { "message": "$COST$ par lietotāju", @@ -3225,13 +3225,13 @@ "message": "Uzzināt vairāk" }, "deleteRecoverDesc": { - "message": "Ievadīt e-pasta adresi zemāk esošajā laukā, lai atkoptu un izdzēstu kontu." + "message": "Jāievada e-pasta adrese zemāk esošajā laukā, lai atkoptu un izdzēstu savu kontu." }, "deleteRecoverEmailSent": { "message": "Ja konts pastāv, tika nosūtīts e-pasta ziņojums ar turpmākām norādēm." }, "deleteRecoverConfirmDesc": { - "message": "Tika pieprasīts dzēst Bitwarden kontu. Nospiest zemāk esošo pogu, lai apstiprinātu." + "message": "Tika pieprasīts izdzēst Bitwarden kontu. Jānospiež zemāk esošā poga, lai apstiprinātu." }, "myOrganization": { "message": "Mana apvienība" @@ -3522,7 +3522,7 @@ "message": "Nekas nav atlasīts." }, "acceptPolicies": { - "message": "Atzīmējot šo rūtiņu, Tu piekrīti sekojošajam:" + "message": "Ar šīs rūtiņas atzīmēšanu tiek piekrists sekojošajam:" }, "acceptPoliciesRequired": { "message": "Nav apstiprināti izmantošanas noteikumi un privātuma nosacījumi." @@ -3681,7 +3681,7 @@ "message": "API atslēga var tikt izmantota, lai autentificētos Bitwarden CLI." }, "userApiKeyWarning": { - "message": "API atslēga ir papildus autentificēšanās risinājums. To vajadzētu glabāt noslēpumā." + "message": "API atslēga ir papildu autentificēšanās risinājums. To vajadzētu glabāt noslēpumā." }, "oauth2ClientCredentials": { "message": "OAuth 2.0 klienta akreditācijas dati", diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 107b604585b..f69f75eb980 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -1426,10 +1426,10 @@ "message": "Proteja a sua conta exigindo uma etapa adicional ao iniciar sessão." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Ative o login em duas etapas para sua organização." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Forçar as opções de login em duas etapas do Bitwarden para membros usando o ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 133373fc64e..602ce5a524c 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -2403,7 +2403,7 @@ "message": "Acompanhe as ações dos utilizadores com registos de auditoria" }, "enforce2faDuo": { - "message": "Impor 2FA com a Duo" + "message": "Aplicar 2FA com a Duo" }, "priorityCustomerSupport": { "message": "Prioridade no apoio ao cliente" diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index d6b97c70418..6c4da856faa 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -1426,14 +1426,14 @@ "message": "Oturum açarken ek bir adım talep ederek hesabınızı güvenceye alabilirsiniz." }, "twoStepLoginTeamsDesc": { - "message": "Kuruluşunuz için iki adımlı oturum açmayı etkinleştirin." + "message": "Kuruluşunuz için iki aşamalı girişi etkinleştirin." }, "twoStepLoginEnterpriseDescStart": { - "message": "Bitwarden'ı kullanarak üyeler için İki Adımlı Giriş seçeneklerini zorunlu kılın ", + "message": "Üyeler için Bitwarden iki aşamalı giriş seçeneklerini zorunlu kılmak üzere ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { - "message": "iki aşamalı giriş ilkesi ile zorunlu tut" + "message": "iki aşamalı giriş ilkesini kullan" }, "twoStepLoginOrganizationDuoDesc": { "message": "Duo ile iki aşamalı girişi zorunlu tutmak için aşağıdaki seçenekleri kullanın." diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 7a4ecc7db08..2136611a2be 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -582,7 +582,7 @@ "message": "访问权限" }, "loggedOut": { - "message": "已登出" + "message": "已注销" }, "loginExpired": { "message": "您的登录会话已过期。" @@ -872,7 +872,7 @@ "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "使用任何 WebAuthn 兼容的安全钥匙访问您的帐户。" + "message": "使用任何 WebAuthn 兼容的安全钥匙访问您的账户。" }, "webAuthnMigrated": { "message": "(迁移自 FIDO)" @@ -1225,7 +1225,7 @@ "message": "接下来的操作会删除组织密码库中的所有项目。" }, "purgeVaultWarning": { - "message": "清空密码库是永久性的。不能被撤消。" + "message": "清空密码库是永久性操作,无法撤销!" }, "vaultPurged": { "message": "密码库已清空。" @@ -1237,7 +1237,7 @@ "message": "接下来的操作会删除您的账户和所有密码库数据。" }, "deleteAccountWarning": { - "message": "删除账户是永久性的。不能被撤消。" + "message": "删除账户是永久性操作,无法撤销!" }, "accountDeleted": { "message": "账户已删除" @@ -1429,7 +1429,7 @@ "message": "为您的组织启用两步登录。" }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "要为成员实施 Bitwarden 两步登录选项,请使用 ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1534,7 +1534,7 @@ "message": "此两步登录提供程序已停用。" }, "twoFactorYubikeyAdd": { - "message": "添加一个新的 YubiKey 到您的帐户" + "message": "添加一个新的 YubiKey 到您的账户" }, "twoFactorYubikeyPlugIn": { "message": "将 YubiKey 插入您电脑的 USB 端口。" @@ -1912,7 +1912,7 @@ "message": "请确保您的账户有足够的信用额度来用于此购买。如果您的账户信用额度不足,您的默认付款方式将用于补足差额。您可以从计费页面向您的账户添加信用额度。" }, "creditAppliedDesc": { - "message": "您帐户的信用额度可用于进行消费。任何可用的信用额度将用于自动支付此帐户的账单。" + "message": "您账户的信用额度可用于进行消费。任何可用的信用额度将用于自动支付此账户的账单。" }, "goPremium": { "message": "升级高级会员", @@ -3255,7 +3255,7 @@ "message": "删除后用户账户仍可用,但不再与此组织关联。" }, "deletingOrganizationIsPermanentWarning": { - "message": "删除 $ORGANIZATION$ 是永久操作,不可撤销!", + "message": "删除 $ORGANIZATION$ 是永久性操作,无法撤销!", "placeholders": { "organization": { "content": "$1", @@ -3357,10 +3357,10 @@ "message": "输入您的安装 ID" }, "limitSubscriptionDesc": { - "message": "为您的订阅设置席位限制。达到此限制后,您将无法邀请新的成员。" + "message": "为您的订阅设置一个席位限制。达到此限制后,您将无法邀请新的成员。" }, "limitSmSubscriptionDesc": { - "message": "为您的机密管理器订阅设置席位限制。达到此限制后,您将无法邀请新的成员。" + "message": "为您的机密管理器订阅设置一个席位限制。达到此限制后,您将无法邀请新的成员。" }, "maxSeatLimit": { "message": "席位限制(可选)", @@ -3669,7 +3669,7 @@ "message": "API 密钥" }, "apiKeyDesc": { - "message": "您的 API 密钥可用于认证 Bitwarden 公共 API。" + "message": "您的 API 密钥可用于验证 Bitwarden 公共 API。" }, "apiKeyRotateDesc": { "message": "轮换 API 密钥将使前一个密钥无效。如果你认为当前密钥不再安全,你可以轮换您的 API 密钥。" @@ -4264,7 +4264,7 @@ "message": "组织的所有者和管理员豁免此策略。" }, "personalOwnershipSubmitError": { - "message": "由于企业策略,您被限制为保存项目到您的个人密码库。将所有权选项更改为组织,并从可用的集合中选择。" + "message": "由于某个企业策略,您不能将项目保存到您的个人密码库。将所有权选项更改为组织,并从可用的集合中选择。" }, "disableSend": { "message": "禁用 Send" @@ -4281,7 +4281,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "由于企业策略,您只能删除现有的 Send。", + "message": "由于某个企业策略,您只能删除现有的 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendOptions": { @@ -4535,7 +4535,7 @@ "message": "注册账户恢复" }, "enrolledAccountRecovery": { - "message": "注册了账户恢复" + "message": "已注册账户恢复" }, "withdrawAccountRecovery": { "message": "撤销账户恢复" @@ -4622,7 +4622,7 @@ "message": "基于加密方式,当主密码或受信任设备被遗忘或丢失时恢复账户。" }, "accountRecoveryPolicyWarning": { - "message": "对于具有主密码的现有账户,需要他们自行注册后,管理员才可以恢复他们的账户。对于新的成员,将自动注册以打开账户恢复功能。" + "message": "对于具有主密码的现有账户,需要成员自行注册后,管理员才可以恢复他们的账户。对于新的成员,自动注册后将打开账户恢复功能。" }, "accountRecoverySingleOrgRequirementDesc": { "message": "激活此策略前,需先开启「单一组织」企业策略。" @@ -4643,13 +4643,13 @@ "message": "重置密码详细信息响应为空" }, "trashCleanupWarning": { - "message": "回收站中超过 30 天的项目将会被自动删除。" + "message": "回收站中超过 30 天的条目将会被自动删除。" }, "trashCleanupWarningSelfHosted": { "message": "回收站中超过一定时间的项目将会被自动删除。" }, "passwordPrompt": { - "message": "重新询问主密码" + "message": "主密码重新提示" }, "passwordConfirmation": { "message": "确认主密码" @@ -4987,7 +4987,7 @@ "message": "权威机构" }, "clientId": { - "message": "客户 ID" + "message": "客户端 ID" }, "clientSecret": { "message": "客户端机密" @@ -5056,7 +5056,7 @@ "message": "单点登录服务 URL" }, "idpSingleLogoutServiceUrl": { - "message": "单点登出服务 URL" + "message": "单点注销服务 URL" }, "idpX509PublicCert": { "message": "X509 公共证书" @@ -5278,10 +5278,10 @@ "message": "「SSO 登录和 Key Connector 解密」已启用。此策略仅适用于所有者和管理员。" }, "enabledSso": { - "message": "SSO 已启用" + "message": "启用了 SSO" }, "disabledSso": { - "message": "SSO 已停用" + "message": "停用了 SSO" }, "enabledKeyConnector": { "message": "Key Connector 已启用" @@ -5428,7 +5428,7 @@ "message": "如果实体 ID 不是一个 URL,则必填。" }, "openIdOptionalCustomizations": { - "message": "自定义(可选)" + "message": "个性化(可选)" }, "openIdAuthorityRequired": { "message": "如果授权无效,则必填。" @@ -5985,7 +5985,7 @@ "description": "Action to view the details of a service account." }, "deleteServiceAccountDialogMessage": { - "message": "删除服务账户 $SERVICE_ACCOUNT$ 是永久性的,无法撤销!", + "message": "删除服务账户 $SERVICE_ACCOUNT$ 是永久性操作,无法撤销!", "placeholders": { "service_account": { "content": "$1", @@ -5994,7 +5994,7 @@ } }, "deleteServiceAccountsDialogMessage": { - "message": "删除服务账户是永久性的,无法撤销!" + "message": "删除服务账户是永久性操作,无法撤销!" }, "deleteServiceAccountsConfirmMessage": { "message": "删除 $COUNT$ 个服务账户", @@ -6097,7 +6097,7 @@ "description": "Notifies that a project has been deleted" }, "deleteProjectDialogMessage": { - "message": "删除工程 $PROJECT$ 是永久性的,无法撤销!", + "message": "删除工程 $PROJECT$ 是永久性操作,无法撤销!", "description": "Informs users that projects are hard deleted and not sent to trash", "placeholders": { "project": { @@ -6137,7 +6137,7 @@ } }, "deleteProjectsDialogMessage": { - "message": "删除工程是永久性的,无法撤销!", + "message": "删除工程是永久性操作,无法撤销!", "description": "This message is displayed in a dialog box as a warning before proceeding with project deletion." }, "projectsNoItemsTitle": { @@ -6223,7 +6223,7 @@ "message": "吊销访问令牌" }, "revokeAccessTokenDesc": { - "message": "吊销访问令牌是永久性的,无法撤销!" + "message": "吊销访问令牌是永久性操作,无法撤销!" }, "accessTokenRevoked": { "message": "访问令牌已吊销", @@ -6354,7 +6354,7 @@ "message": "自动域验证" }, "automaticDomainVerificationProcess": { - "message": "Bitwarden 将尝试在 72 小时内验证此域 3 次。如果无法验证此域,请检查您的主机中的 DNS 记录并手动验证。如果此域无法验证,7 天内将从您的组织中移除。" + "message": "Bitwarden 将尝试在 72 小时内验证此域 3 次。如果此域无法验证,请检查您的主机中的 DNS 记录并手动验证。如果此域未通过验证,7 天内将从您的组织中移除。" }, "invalidDomainNameMessage": { "message": "输入格式不正确。格式:mydomain.com。子域需要单独的条目进行验证。" @@ -6387,7 +6387,7 @@ } }, "domainNotVerified": { - "message": "$DOMAIN$ 无法验证。请检查您的 DNS 记录。", + "message": "$DOMAIN$ 未通过验证。请检查您的 DNS 记录。", "placeholders": { "DOMAIN": { "content": "$1", @@ -6399,7 +6399,7 @@ "message": "已验证" }, "domainStatusUnverified": { - "message": "无法验证" + "message": "未验证" }, "domainNameTh": { "message": "域名" @@ -6417,7 +6417,7 @@ "message": "表单错误需要您注意" }, "addedDomain": { - "message": "已添加域 $DOMAIN$", + "message": "添加了域 $DOMAIN$", "placeholders": { "DOMAIN": { "content": "$1", @@ -6444,7 +6444,7 @@ } }, "domainNotVerifiedEvent": { - "message": "$DOMAIN$ 无法验证", + "message": "$DOMAIN$ 未通过验证", "placeholders": { "DOMAIN": { "content": "$1", @@ -6866,10 +6866,10 @@ "message": "使用主密码批准" }, "trustedDeviceEncryption": { - "message": "受信任的设备加密" + "message": "受信任设备加密" }, "trustedDevices": { - "message": "受信任的设备" + "message": "受信任设备" }, "memberDecryptionOptionTdeDescriptionPartOne": { "message": "验证后,成员将使用存储在他们设备上的密钥解密密码库数据。使用此选项后,", @@ -7124,7 +7124,7 @@ } }, "addAdditionalServiceAccounts": { - "message": "您可以以 $COST$ 每月购买附加服务账户。", + "message": "您也可以以 $COST$ 每月购买附加服务账户。", "placeholders": { "cost": { "content": "$1", diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index f7692a0a3e4..dde9ea4e746 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -226,7 +226,7 @@ "message": "檢查密碼是否已暴露。" }, "passwordExposed": { - "message": "此密碼在資料外洩事件中被暴露了 $VALUE$ 次,應立即變更。", + "message": "此密碼在資料外洩事件中被暴露了 $VALUE$ 次,應立即變更它。", "placeholders": { "value": { "content": "$1", @@ -383,7 +383,7 @@ "message": "選擇" }, "newItem": { - "message": "新項目" + "message": "新增項目" }, "addItem": { "message": "新增項目" @@ -473,10 +473,10 @@ "message": "移動選取的項目至組織" }, "deleteSelected": { - "message": "刪除選取的項目" + "message": "刪除所選" }, "moveSelected": { - "message": "移動選取的項目" + "message": "移動所選" }, "selectAll": { "message": "全選" @@ -687,7 +687,7 @@ "message": "必須再次輸入主密碼。" }, "masterPasswordMinlength": { - "message": "主密碼至少需 $VALUE$ 個字元。", + "message": "主密碼必須至少 $VALUE$ 個字元。", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -712,7 +712,7 @@ "message": "發生了未預期的錯誤。" }, "expirationDateError": { - "message": "Please select an expiration date that is in the future." + "message": "請選擇一個未來的逾期日期。" }, "emailAddress": { "message": "電子郵件地址" @@ -902,7 +902,7 @@ "message": "編輯與此項目共享的集合。只有具有這些集合存取權限的組織使用者才能夠看到此項目。" }, "deleteSelectedItemsDesc": { - "message": "您已經選取了 $COUNT$ 個項目。確定要刪除這些項目嗎 ?", + "message": "$COUNT$ 個項目將被移至垃圾桶。", "placeholders": { "count": { "content": "$1", @@ -955,7 +955,7 @@ "message": "複製驗證碼" }, "copyUuid": { - "message": "Copy UUID" + "message": "複製 UUID" }, "warning": { "message": "警告" @@ -964,7 +964,7 @@ "message": "確認匯出密碼庫" }, "confirmSecretsExport": { - "message": "Confirm secrets export" + "message": "確認匯出機密" }, "exportWarningDesc": { "message": "此次匯出的密碼庫檔案為未加密格式。您不應將它存放或經由不安全的方式(例如電子郵件)傳送。用完後請立即將它刪除。" @@ -985,7 +985,7 @@ "message": "匯出密碼庫" }, "exportSecrets": { - "message": "Export secrets" + "message": "匯出機密" }, "fileFormat": { "message": "檔案格式" @@ -1009,16 +1009,16 @@ "message": "確認檔案密碼" }, "accountRestrictedOptionDescription": { - "message": "使用衍生自您帳號的使用者名稱與主密碼的加密密鑰,加密此匯出檔案,並限制只能匯入到目前的 Bitwarden 帳號。" + "message": "使用衍生自您帳戶的使用者名稱與主密碼的加密金鑰,加密此匯出檔案,並限制只能匯入到目前的 Bitwarden 帳戶。" }, "passwordProtectedOptionDescription": { - "message": "設定一組密碼來加密匯出的資料,並使用此密碼解密以匯入至任意 Bitwarden 帳戶。" + "message": "設定一組檔案密碼來加密匯出的資料,並使用此密碼解密以匯入至任意 Bitwarden 帳戶。" }, "exportTypeHeading": { "message": "匯出類型" }, "accountRestricted": { - "message": "帳號已限制" + "message": "帳戶已限制" }, "passwordProtected": { "message": "密碼保護" @@ -1294,16 +1294,16 @@ "message": "解密匯出的檔案時發生錯誤,您的加密金鑰與匯出資料時使用的金鑰不同。" }, "importDestination": { - "message": "Import destination" + "message": "匯入目的地" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "瞭解更多匯入選項" }, "selectImportFolder": { - "message": "Select a folder" + "message": "選擇一個資料夾" }, "selectImportCollection": { - "message": "Select a collection" + "message": "選擇一個集合" }, "importTargetHint": { "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", @@ -1316,7 +1316,7 @@ } }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "檔案包含未指派的項目。" }, "selectFormat": { "message": "選擇匯入檔案的格式" @@ -1426,7 +1426,7 @@ "message": "在登入時執行額外的步驟來保護您的帳戶。" }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "爲您的組織啟用兩步驟登入。" }, "twoStepLoginEnterpriseDescStart": { "message": "Enforce Bitwarden Two-step Login options for members by using the ", @@ -1890,7 +1890,7 @@ "message": "付款類型" }, "accountCredit": { - "message": "帳戶信用額度", + "message": "帳戶餘額", "description": "Financial term. In the case of Bitwarden, a positive balance means that you owe money, while a negative balance means that you have a credit (Bitwarden owes you money)." }, "accountBalance": { @@ -2269,7 +2269,7 @@ "message": "使用者席位" }, "additionalUserSeats": { - "message": "附加使用者席位" + "message": "額外使用者席位" }, "userSeatsDesc": { "message": "# 使用者席位" @@ -2499,7 +2499,7 @@ "message": "群組" }, "newGroup": { - "message": "新群組" + "message": "新增群組" }, "addGroup": { "message": "新增群組" @@ -2634,13 +2634,13 @@ "message": "使用者" }, "userDesc": { - "message": "存取受指派的集合,並新增項目到集合中" + "message": "存取和新增項目到已指派的集合中" }, "manager": { "message": "管理員" }, "managerDesc": { - "message": "管理員可以存取和管理組織内已指派的集合。" + "message": "在已指派的集合中創建、刪除以及管理存取權限" }, "all": { "message": "全部" @@ -2676,7 +2676,7 @@ "message": "網頁版密碼庫" }, "bitWebVault": { - "message": "Bitwarden Web vault" + "message": "Bitwarden 網頁密碼庫" }, "bitSecretsManager": { "message": "Bitwarden Secrets Manager" @@ -2703,10 +2703,10 @@ "message": "嘗試登入失敗,兩步驟登入錯誤。" }, "incorrectPassword": { - "message": "Incorrect password" + "message": "密碼不正確" }, "incorrectCode": { - "message": "Incorrect code" + "message": "驗證碼不正確" }, "exportedVault": { "message": "已匯出密碼庫" @@ -2802,7 +2802,7 @@ } }, "viewCollectionWithName": { - "message": "View collection - $NAME$", + "message": "檢視集合 - $NAME$", "placeholders": { "name": { "content": "$1", @@ -2811,7 +2811,7 @@ } }, "editItemWithName": { - "message": "Edit item - $NAME$", + "message": "編輯項目 - $NAME$", "placeholders": { "name": { "content": "$1", @@ -3096,7 +3096,7 @@ "message": "發生錯誤。" }, "userAccess": { - "message": "使用者存取" + "message": "使用者存取權限" }, "userType": { "message": "使用者類型" @@ -3105,7 +3105,7 @@ "message": "群組存取" }, "groupAccessUserDesc": { - "message": "編輯此使用者所屬的群組。" + "message": "透過將成員添加到 1 個或多個群組來授予他們對集合的存取權限。" }, "invitedUsers": { "message": "已邀請使用者。" @@ -3543,7 +3543,7 @@ "message": "選擇密碼庫何時執行密碼庫逾時動作。" }, "vaultTimeoutLogoutDesc": { - "message": "Choose when your vault will be logged out." + "message": "選擇您的密碼庫何時登出。" }, "oneMinute": { "message": "1 分鐘" @@ -3634,7 +3634,7 @@ "message": "此項目包含需要修正的舊檔案附件。" }, "attachmentFixDescription": { - "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." + "message": "此附件使用了過時的加密方式。 選擇「修正」以下載、重新加密,然後重新上傳此附件。" }, "fix": { "message": "修正", @@ -4122,7 +4122,7 @@ "message": "您是一個或多個組織的擁有者。如果您授予緊急聯絡人接管存取權限,那麼他們在接管後將能夠以組織擁有者的身分使用您的所有權限。" }, "trustedEmergencyContacts": { - "message": "信任的緊急聯絡人" + "message": "可信任的緊急聯絡人" }, "noTrustedContacts": { "message": "您尚未新增任何緊急聯絡人,請邀請一位可信任的聯絡人以開始。" @@ -4203,7 +4203,7 @@ "message": "要求存取權限" }, "requestAccessConfirmation": { - "message": "您確定要申請緊急存取嗎?在 $WAITTIME$ 天之後或使用者手動批准要求時您將取得存取權限。", + "message": "您確定要申請緊急存取嗎?在 $WAITTIME$ 天之後或使用者手動核准要求時您將取得存取權限。", "placeholders": { "waittime": { "content": "$1", @@ -4325,7 +4325,7 @@ "message": "自訂" }, "customDesc": { - "message": "進階設定允許更精細地控制使用者權限。" + "message": "為成員授予自訂權限" }, "customDescNonEnterpriseStart": { "message": "自訂角色為 ", @@ -4400,7 +4400,7 @@ "message": "管理使用者" }, "manageAccountRecovery": { - "message": "Manage account recovery" + "message": "管理帳戶復原" }, "disableRequiredError": { "message": "您必須先手動停用 $POLICYNAME$ 原則,然後才能停用此原則。", @@ -4532,13 +4532,13 @@ "message": "密碼提示不能與您的密碼相同。" }, "enrollAccountRecovery": { - "message": "Enroll in account recovery" + "message": "注冊帳戶復原" }, "enrolledAccountRecovery": { - "message": "Enrolled in account recovery" + "message": "已注冊帳戶復原" }, "withdrawAccountRecovery": { - "message": "Withdraw from account recovery" + "message": "撤銷帳戶復原" }, "enrollPasswordResetSuccess": { "message": "註冊成功!" @@ -4547,7 +4547,7 @@ "message": "撤銷成功!" }, "eventEnrollAccountRecovery": { - "message": "User $ID$ enrolled in account recovery.", + "message": "使用者 $ID$ 注冊了帳戶復原。", "placeholders": { "id": { "content": "$1", @@ -4556,7 +4556,7 @@ } }, "eventWithdrawAccountRecovery": { - "message": "User $ID$ withdrew from account recovery.", + "message": "使用者 $ID$ 撤銷了帳戶復原", "placeholders": { "id": { "content": "$1", @@ -4616,13 +4616,13 @@ "message": "註冊後將允許組織管理者變更您的主密碼" }, "accountRecoveryPolicy": { - "message": "Account recovery administration" + "message": "帳戶復原管理" }, "accountRecoveryPolicyDesc": { - "message": "依照加密方式,當忘記主密碼或遺失受信任的裝置時,可救回帳號。" + "message": "依照加密方式,當忘記主密碼或遺失受信任的裝置時,可救回帳戶。" }, "accountRecoveryPolicyWarning": { - "message": "現有主密碼的帳號,需要使用者自行加入後,管理員才可發起帳號救援。將為新使用者開啟自動加入。" + "message": "對於具有主密碼的現有帳戶,需要成員自行註冊後,管理員才可復原其帳戶。將為新成員開啟自動注冊以啟用帳戶復原功能。" }, "accountRecoverySingleOrgRequirementDesc": { "message": "必須先開啟「單一組織」企業原則,才能開啟此原則。" @@ -4643,10 +4643,10 @@ "message": "重設密碼詳細資訊回應為 null" }, "trashCleanupWarning": { - "message": "垃圾桶中超過 30 天的密碼將會被自動刪除。" + "message": "垃圾桶中超過 30 天的項目將會被自動刪除。" }, "trashCleanupWarningSelfHosted": { - "message": "垃圾桶中超過一定時間的密碼將會被自動刪除。" + "message": "垃圾桶中超過一定時間的項目將會被自動刪除。" }, "passwordPrompt": { "message": "重新詢問主密碼" @@ -4691,7 +4691,7 @@ "message": "淺色" }, "confirmSelected": { - "message": "確認選取" + "message": "確認所選" }, "bulkConfirmStatus": { "message": "批次作業狀態" @@ -4730,7 +4730,7 @@ "message": "錯誤" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "管理使用者也必須被授予管理帳戶復原的權限" }, "setupProvider": { "message": "提供者設定" @@ -4951,16 +4951,16 @@ "message": "一個或多個組織原則禁止您匯出個人密碼庫。" }, "activateAutofill": { - "message": "Activate auto-fill" + "message": "啓用自動填入" }, "activateAutofillPolicyDesc": { - "message": "Activate the auto-fill on page load setting on the browser extension for all existing and new members." + "message": "為所有現有的和新的成員啓用瀏覽器擴充套件上的頁面載入時自動填入設定。" }, "experimentalFeature": { - "message": "Compromised or untrusted websites can exploit auto-fill on page load." + "message": "被入侵或不可信任的網站可以利用網頁載入時的自動填入功能。" }, "learnMoreAboutAutofill": { - "message": "Learn more about auto-fill" + "message": "進一步瞭解「自動填入」功能" }, "selectType": { "message": "選擇 SSO 類型" @@ -5092,7 +5092,7 @@ "message": "最多 6 位使用者的付費方案存取權限" }, "sponsoredFamiliesSharedCollections": { - "message": "用於家庭共用帳號密碼的集合" + "message": "用於家庭機密的共用集合" }, "badToken": { "message": "連結已失效。請讓贊助者重新傳送邀請。" @@ -5365,7 +5365,7 @@ "message": "權杖已輪換" }, "billingSyncDesc": { - "message": "「帳單同步」藉由將您的自我裝載 Bitwarden 服務與 Bitwarden 雲端伺服器連結,向成員提供「免費家庭」方案及進階帳單功能。" + "message": "「帳單同步」解鎖家庭贊助並在您的伺服器上自動同步授權。在 Bitwarden 雲端伺服器上更新后,請選擇「同步授權」以應用變更。" }, "billingSyncKeyDesc": { "message": "想要完成這個表單,需要您雲端組織訂閱設定中的「帳單同步權杖」。" @@ -5428,7 +5428,7 @@ "message": "若 Entity ID 非 URL,則必須填入。" }, "openIdOptionalCustomizations": { - "message": "選用自訂項目" + "message": "自訂(選填)" }, "openIdAuthorityRequired": { "message": "若授權無效,則必須填入。" @@ -5489,7 +5489,7 @@ "description": "This is used by screen readers to indicate the organization that is currently being shown to the user." }, "accountLoggedInAsName": { - "message": "Account: Logged in as $NAME$", + "message": "帳戶:已登入為 $NAME$", "placeholders": { "name": { "content": "$1", @@ -5636,7 +5636,7 @@ "description": "The text, 'SCIM', is an acronymn and should not be translated." }, "scimDescription": { - "message": "透過 SCIM 佈建,使用您首選的身分提供程式自動佈建用戶和群組", + "message": "透過 SCIM 佈建,使用您首選的身分提供程式自動佈建使用者和群組", "description": "the text, 'SCIM', is an acronymn and should not be translated." }, "scimEnabledCheckboxDesc": { @@ -5710,7 +5710,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "以下字元不被允許:$CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -5719,7 +5719,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "輸入的值不能低於 $MIN$。", "placeholders": { "min": { "content": "$1", @@ -5737,10 +5737,10 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "一個或多個電子郵件無效" }, "tooManyEmails": { - "message": "You can only submit up to $COUNT$ emails at a time", + "message": "您一次最多只能送出 $COUNT$ 個電子郵件", "placeholders": { "count": { "content": "$1", @@ -5876,13 +5876,13 @@ "description": "The action to delete multiple secrets from the system." }, "hardDeleteSecret": { - "message": "Permanently delete secret" + "message": "永久刪除機密" }, "hardDeleteSecrets": { - "message": "Permanently delete secrets" + "message": "永久刪除機密" }, "secretProjectAssociationDescription": { - "message": "選擇與機密相關聯的項目。僅有權存取該項目的組織使用者能檢視該機密。", + "message": "選擇與機密相關聯的專案。僅有權存取該專案的組織使用者能檢視該機密。", "description": "A prompt explaining how secrets can be associated with projects." }, "selectProjects": { @@ -5894,7 +5894,7 @@ "description": "Label for the search bar used to search projects." }, "project": { - "message": "項目", + "message": "專案", "description": "Similar to collections, projects can be used to group secrets." }, "editProject": { @@ -5918,11 +5918,11 @@ "description": "Label for a secret (key/value pair)" }, "serviceAccount": { - "message": "服務帳號", + "message": "服務帳戶", "description": "A machine user which can be used to automate processes and access secrets in the system." }, "serviceAccounts": { - "message": "服務帳號", + "message": "服務帳戶", "description": "The title for the section that deals with service accounts." }, "secrets": { @@ -5946,7 +5946,7 @@ "description": "Title for creating a new secret." }, "newServiceAccount": { - "message": "新增服務帳號", + "message": "新增服務帳戶", "description": "Title for creating a new service account." }, "secretsNoItemsTitle": { @@ -5958,7 +5958,7 @@ "description": "Message to encourage the user to start adding secrets." }, "secretsTrashNoItemsMessage": { - "message": "There are no secrets in the trash." + "message": "垃圾桶中沒有機密。" }, "serviceAccountsNoItemsMessage": { "message": "Create a new service account to get started automating secret access.", @@ -5973,19 +5973,19 @@ "description": "Placeholder text for searching secrets." }, "deleteServiceAccounts": { - "message": "刪除服務帳號", + "message": "刪除服務帳戶", "description": "Title for the action to delete one or multiple service accounts." }, "deleteServiceAccount": { - "message": "刪除服務帳號", + "message": "刪除服務帳戶", "description": "Title for the action to delete a single service account." }, "viewServiceAccount": { - "message": "檢視服務帳號", + "message": "檢視服務帳戶", "description": "Action to view the details of a service account." }, "deleteServiceAccountDialogMessage": { - "message": "Deleting service account $SERVICE_ACCOUNT$ is permanent and irreversible.", + "message": "刪除服務帳戶 $SERVICE_ACCOUNT$ 為永久性動作,並且無法恢復。", "placeholders": { "service_account": { "content": "$1", @@ -5994,10 +5994,10 @@ } }, "deleteServiceAccountsDialogMessage": { - "message": "Deleting service accounts is permanent and irreversible." + "message": "刪除服務帳戶為永久性動作,並且無法恢復。" }, "deleteServiceAccountsConfirmMessage": { - "message": "Delete $COUNT$ service accounts", + "message": "刪除 $COUNT$ 組服務帳戶", "placeholders": { "count": { "content": "$1", @@ -6006,17 +6006,17 @@ } }, "deleteServiceAccountToast": { - "message": "Service account deleted" + "message": "服務帳戶已刪除" }, "deleteServiceAccountsToast": { - "message": "Service accounts deleted" + "message": "服務帳戶已刪除" }, "searchServiceAccounts": { - "message": "搜尋服務帳號", + "message": "搜尋服務帳戶", "description": "Placeholder text for searching service accounts." }, "editServiceAccount": { - "message": "Edit service account", + "message": "編輯服務帳戶", "description": "Title for editing a service account." }, "addProject": { @@ -6032,7 +6032,7 @@ "description": "Notification for the successful saving of a project." }, "projectCreated": { - "message": "項目已建立", + "message": "專案已建立", "description": "Notification for the successful creation of a project." }, "projectName": { @@ -6052,36 +6052,36 @@ "description": "Notifies that the selected secrets have been moved to the trash" }, "hardDeleteSecretConfirmation": { - "message": "Are you sure you want to permanently delete this secret?" + "message": "您確定要永久刪除此機密嗎?" }, "hardDeleteSecretsConfirmation": { - "message": "Are you sure you want to permanently delete these secrets?" + "message": "您確定要永久刪除這些機密嗎?" }, "hardDeletesSuccessToast": { - "message": "Secrets permanently deleted" + "message": "機密已永久刪除" }, "smAccess": { "message": "存取", "description": "Title indicating what permissions a service account has" }, "projectCommaSecret": { - "message": "項目,機密", + "message": "專案,機密", "description": "" }, "serviceAccountName": { - "message": "服務帳號名稱", + "message": "服務帳戶名稱", "description": "Label for the name of a service account" }, "serviceAccountCreated": { - "message": "服務帳號已建立", + "message": "服務帳戶已建立", "description": "Notifies that a new service account has been created" }, "serviceAccountUpdated": { - "message": "Service account updated", + "message": "服務帳戶已更新", "description": "Notifies that a service account has been updated" }, "newSaSelectAccess": { - "message": "輸入或選擇項目或機密", + "message": "輸入或選擇專案或機密", "description": "Instructions for selecting projects or secrets for a new service account" }, "newSaTypeToFilter": { @@ -6137,7 +6137,7 @@ } }, "deleteProjectsDialogMessage": { - "message": "刪除項目為永久性動作,並且無法恢復。", + "message": "刪除專案為永久性動作,並且無法恢復。", "description": "This message is displayed in a dialog box as a warning before proceeding with project deletion." }, "projectsNoItemsTitle": { @@ -6161,7 +6161,7 @@ "description": "Notification to be displayed when a secret is successfully sent to the trash." }, "hardDeleteSuccessToast": { - "message": "Secret permanently deleted" + "message": "機密已永久刪除" }, "accessTokens": { "message": "存取權杖", @@ -6192,7 +6192,7 @@ "description": "Message to be displayed before closing an access token, reminding the user to download or copy it." }, "expiresOnAccessToken": { - "message": "到期日:", + "message": "逾期日:", "description": "Label for the expiration date of an access token." }, "accessTokenCallOutTitle": { @@ -6208,7 +6208,7 @@ "description": "A unique string that gives a client application (eg. CLI) access to a secret or set of secrets." }, "accessTokenExpirationRequired": { - "message": "到期日期是必需的。", + "message": "必須指定逾期日期。", "description": "Error message indicating that an expiration date for the access token must be set." }, "accessTokenCreatedAndCopied": { @@ -6220,17 +6220,17 @@ "description": "Invalidates / cancels an access token and as such removes access to secrets for the client application." }, "revokeAccessTokens": { - "message": "Revoke access tokens" + "message": "撤銷存取權杖" }, "revokeAccessTokenDesc": { - "message": "Revoking access tokens is permanent and irreversible." + "message": "撤銷存取權杖為永久性動作,並且無法恢復。" }, "accessTokenRevoked": { - "message": "Access tokens revoked", + "message": "存取權杖已撤銷", "description": "Toast message after deleting one or multiple access tokens." }, "noAccessTokenSelected": { - "message": "No access token selected to revoke", + "message": "沒有選擇要撤銷的存取權杖", "description": "Toast error message after trying to delete access tokens but not selecting any access tokens." }, "submenu": { @@ -6261,13 +6261,13 @@ "message": "群組資訊" }, "editGroupMembersDesc": { - "message": "Grant members access to the group's assigned collections." + "message": "授予成員對該群組已指派的集合的存取權限。" }, "editGroupCollectionsDesc": { - "message": "Grant access to collections by adding them to this group." + "message": "透過將他們添加到此群組,授予對集合的存取權限。" }, "accessAllCollectionsDesc": { - "message": "Grant access to all current and future collections." + "message": "授予對所有目前和未來的集合的存取權限。" }, "accessAllCollectionsHelp": { "message": "If checked, this will replace all other collection permissions." @@ -6288,7 +6288,7 @@ "message": "集合" }, "noCollection": { - "message": "No collection" + "message": "沒有集合" }, "canView": { "message": "可以檢視" @@ -6303,13 +6303,13 @@ "message": "可以編輯(除了密碼)" }, "noCollectionsAdded": { - "message": "No collections added" + "message": "未新增任何集合" }, "noMembersAdded": { - "message": "No members added" + "message": "未新增任何成員" }, "noGroupsAdded": { - "message": "No groups added" + "message": "未新增任何群組" }, "group": { "message": "群組" @@ -6324,7 +6324,7 @@ "message": "網域驗證" }, "newDomain": { - "message": "New domain" + "message": "新增網域" }, "noDomains": { "message": "沒有網域" @@ -6363,7 +6363,7 @@ "message": "刪除網域" }, "removeDomainWarning": { - "message": "Removing a domain cannot be undone. Are you sure you want to continue?" + "message": "移除網域后不能復原。您確定要繼續嗎?" }, "domainRemoved": { "message": "網域已移除" @@ -6375,10 +6375,10 @@ "message": "已驗證網域" }, "duplicateDomainError": { - "message": "You can't claim the same domain twice." + "message": "您不能再次聲明同一個網域。" }, "domainNotAvailable": { - "message": "其他人正在使用 $DOMAIN$。請使用一個不同的網域名稱以繼續。", + "message": "其他人正在使用 $DOMAIN$。請使用一個不同的網域以繼續。", "placeholders": { "DOMAIN": { "content": "$1", @@ -6468,7 +6468,7 @@ "message": "Permissions set for a member will replace permissions set by that member's group" }, "noMembersOrGroupsAdded": { - "message": "No members or groups added" + "message": "未新增任何成員或群組" }, "deleted": { "message": "已刪除" @@ -6534,7 +6534,7 @@ "message": "匯出資料" }, "exportingOrganizationSecretDataTitle": { - "message": "Exporting Organization Secret Data" + "message": "匯出組織機密資料" }, "exportingOrganizationSecretDataDescription": { "message": "Only the Secrets Manager data associated with $ORGANIZATION$ will be exported. Items in other products or from other organizations will not be included.", @@ -6549,7 +6549,7 @@ "message": "檔案上傳" }, "acceptedFormats": { - "message": "Accepted Formats:" + "message": "接受的格式:" }, "copyPasteImportContents": { "message": "Copy & paste import contents:" @@ -6558,7 +6558,7 @@ "message": "或" }, "licenseAndBillingManagement": { - "message": "License and billing management" + "message": "授權和帳單管理" }, "automaticSync": { "message": "自動同步" @@ -6567,25 +6567,25 @@ "message": "手動上傳" }, "manualUploadDesc": { - "message": "If you do not want to opt into billing sync, manually upload your license here." + "message": "如果您不想選擇帳單同步,請在這裏手動上傳您的授權証。" }, "syncLicense": { - "message": "Sync license" + "message": "同步授權" }, "licenseSyncSuccess": { - "message": "Successfully synced license" + "message": "授權成功同步" }, "licenseUploadSuccess": { - "message": "Successfully uploaded license" + "message": "授權証上傳成功" }, "lastLicenseSync": { - "message": "Last license sync" + "message": "最近授权同步" }, "billingSyncHelp": { - "message": "Billing Sync help" + "message": "帳單同步幫助" }, "licensePaidFeaturesHelp": { - "message": "License paid features help" + "message": "授權付費功能幫助" }, "selfHostGracePeriodHelp": { "message": "After your subscription expires, you have 60 days to apply an updated license file to your organization. Grace period ends $GRACE_PERIOD_END_DATE$.", @@ -6600,37 +6600,37 @@ "message": "上傳授權證" }, "projectPeopleDescription": { - "message": "Grant groups or people access to this project." + "message": "授予群組或人員對此專案的存取權限。" }, "projectPeopleSelectHint": { - "message": "Type or select people or groups" + "message": "輸入或選擇人員或群組" }, "projectServiceAccountsDescription": { - "message": "Grant service accounts access to this project." + "message": "授予服務帳戶對此專案的存取權限。" }, "projectServiceAccountsSelectHint": { - "message": "Type or select service accounts" + "message": "輸入或選擇服務帳戶" }, "projectEmptyPeopleAccessPolicies": { - "message": "Add people or groups to start collaborating" + "message": "新增人員或群組以開始協作" }, "projectEmptyServiceAccountAccessPolicies": { - "message": "Add service accounts to grant access" + "message": "新增服務帳戶以授予存取權限" }, "serviceAccountPeopleDescription": { - "message": "Grant groups or people access to this service account." + "message": "授予群組或人員對此服務帳戶的存取權限。" }, "serviceAccountProjectsDescription": { "message": "Assign projects to this service account. " }, "serviceAccountEmptyProjectAccesspolicies": { - "message": "Add projects to grant access" + "message": "新增專案以授予存取權限" }, "canReadWrite": { - "message": "Can read, write" + "message": "可以讀取,寫入" }, "groupSlashUser": { - "message": "Group/User" + "message": "群組/使用者" }, "lowKdfIterations": { "message": "Low KDF Iterations" @@ -6645,16 +6645,16 @@ "message": "Secrets Manager" }, "secretsManagerBeta": { - "message": "Secrets Manager Beta" + "message": "Secrets Manager Beta 版" }, "secretsManagerAccessDescription": { - "message": "Activate user access to Secrets Manager." + "message": "已激活對 Secrets Manager 的使用者存取權限。" }, "userAccessSecretsManagerGA": { - "message": "This user can access Secrets Manager" + "message": "此使用者可以存取 Secrets Manager" }, "important": { - "message": "Important:" + "message": "重要:" }, "viewAll": { "message": "檢視全部" @@ -6673,16 +6673,16 @@ } }, "resolveTheErrorsBelowAndTryAgain": { - "message": "Resolve the errors below and try again." + "message": "請解決下面的錯誤然後再試一次。" }, "description": { "message": "Description" }, "errorReadingImportFile": { - "message": "An error occurred when trying to read the import file" + "message": "嘗試讀取匯入檔案時發生錯誤" }, "accessedSecret": { - "message": "Accessed secret $SECRET_ID$.", + "message": "存取了機密 $SECRET_ID$。", "placeholders": { "secret_id": { "content": "$1", @@ -6695,23 +6695,23 @@ "description": "Software Development Kit" }, "createSecret": { - "message": "Create a secret" + "message": "建立機密" }, "createProject": { - "message": "Create a project" + "message": "建立專案" }, "createServiceAccount": { - "message": "Create a service account" + "message": "建立服務帳戶" }, "downloadThe": { - "message": "Download the", + "message": "下載", "description": "Link to a downloadable resource. This will be used as part of a larger phrase. Example: Download the Secrets Manager CLI" }, "smCLI": { "message": "Secrets Manager CLI" }, "importSecrets": { - "message": "Import secrets" + "message": "匯入機密" }, "getStarted": { "message": "開始使用" @@ -6736,10 +6736,10 @@ "message": "還原機密" }, "restoreSecretPrompt": { - "message": "Are you sure you want to restore this secret?" + "message": "您確定要還原此機密嗎?" }, "restoreSecretsPrompt": { - "message": "Are you sure you want to restore these secrets?" + "message": "您確定要還原這些機密嗎?" }, "secretRestoredSuccessToast": { "message": "機密已還原" @@ -6754,7 +6754,7 @@ "message": "Turn on organization access to the Secrets Manager at no charge during the Beta program. Users can be granted access to the Beta in Members." }, "secretsManagerEnable": { - "message": "Enable Secrets Manager Beta" + "message": "啟用 Secrets Manager Beta 版" }, "saPeopleWarningTitle": { "message": "Access tokens still available" @@ -6763,31 +6763,31 @@ "message": "Removing people from a service account does not remove the access tokens they created. For security best practice, it is recommended to revoke access tokens created by people removed from a service account." }, "smAccessRemovalWarningProjectTitle": { - "message": "Remove access to this project" + "message": "移除對此專案的存取權限" }, "smAccessRemovalWarningProjectMessage": { - "message": "This action will remove your access to the project." + "message": "此動作將移除您對此專案的訪問權限。" }, "smAccessRemovalWarningSaTitle": { - "message": "Remove access to this service account" + "message": "移除對此服務帳戶的訪問權限" }, "smAccessRemovalWarningSaMessage": { - "message": "This action will remove your access to the service account." + "message": "此動作將移除您對此服務帳戶的訪問權限。" }, "removeAccess": { - "message": "Remove access" + "message": "移除存取權限" }, "checkForBreaches": { "message": "Check known data breaches for this password" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "已暴露的主密碼" }, "exposedMasterPasswordDesc": { - "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + "message": "在資料外洩事件中找到了密碼。我們建議您使用一個獨特的密碼來保護您的帳戶,您確定要使用已暴露的密碼嗎?" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "強度不足且已暴露的主密碼" }, "weakAndBreachedMasterPasswordDesc": { "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" @@ -6802,7 +6802,7 @@ } }, "masterPasswordMinimumlength": { - "message": "Master password must be at least $LENGTH$ characters long.", + "message": "主密碼必須至少 $LENGTH$ 個字元。", "placeholders": { "length": { "content": "$1", @@ -6834,42 +6834,42 @@ "message": "bitwarden.eu" }, "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": { - "message": "The following projects can not be deleted. Would you like to continue?", + "message": "以下專案無法刪除。要繼續嗎?", "description": "The message shown to the user when bulk deleting projects and the user doesn't have access to some projects." }, "updateKdfSettings": { "message": "更新 KDF 設定" }, "loginInitiated": { - "message": "Login initiated" + "message": "登入已發起" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "裝置核准已要求。請選擇一個以下的核准選項:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "記住這個裝置" }, "uncheckIfPublicDevice": { "message": "Uncheck if using a public device" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "從其他裝置批准" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "要求管理員核准" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "使用主密碼批准" }, "trustedDeviceEncryption": { - "message": "Trusted device encryption" + "message": "可信任的裝置加密" }, "trustedDevices": { - "message": "Trusted devices" + "message": "可信任的裝置" }, "memberDecryptionOptionTdeDescriptionPartOne": { "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", @@ -6900,7 +6900,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.'" }, "notFound": { - "message": "$RESOURCE$ not found", + "message": "找不到 $RESOURCE$", "placeholders": { "resource": { "content": "$1", @@ -6909,67 +6909,67 @@ } }, "recoverAccount": { - "message": "Recover account" + "message": "復原帳戶" }, "updatedTempPassword": { - "message": "User updated a password issued through account recovery." + "message": "使用者更新了透過帳戶復原頒發的密碼。" }, "activatedAccessToSecretsManager": { - "message": "Activated access to Secrets Manager", + "message": "已激活對 Secrets Manager 的存取", "description": "Confirmation message that one or more users gained access to Secrets Manager" }, "activateAccess": { "message": "Activate access" }, "bulkEnableSecretsManagerDescription": { - "message": "Grant the following members access to Secrets Manager. The role granted in the Password Manager will apply to Secrets Manager.", + "message": "授予以下成員對 Secrets Manager 的存取權限。在 Password Manager 授予的角色也適用於 Secrets Manager。", "description": "This description is shown to an admin when they are attempting to add more users to Secrets Manager." }, "activateSecretsManager": { - "message": "Activate Secrets Manager" + "message": "激活 Secrets Manager" }, "yourOrganizationsFingerprint": { - "message": "Your organization's fingerprint phrase", + "message": "您組織的指紋短語", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their organization's public key with another user, for the purposes of sharing." }, "deviceApprovals": { - "message": "Device approvals" + "message": "裝置核准" }, "deviceApprovalsDesc": { - "message": "Approve login requests below to allow the requesting member to finish logging in. Unapproved requests expire after 1 week. Verify the member’s information before approving." + "message": "核准以下的登入要求以允許成員完成登入。未核准的要求將在 1 周後逾期。 在核准之前请驗證成員資訊。" }, "deviceInfo": { - "message": "Device info" + "message": "裝置資訊" }, "time": { - "message": "Time" + "message": "時間" }, "denyAllRequests": { - "message": "Deny all requests" + "message": "拒絕所有要求" }, "denyRequest": { - "message": "Deny request" + "message": "拒絕要求" }, "approveRequest": { - "message": "Approve request" + "message": "批准要求" }, "noDeviceRequests": { - "message": "No device requests" + "message": "沒有裝置要求" }, "noDeviceRequestsDesc": { - "message": "Member device approval requests will appear here" + "message": "成員的裝置核准要求將顯示在這裏" }, "loginRequestDenied": { - "message": "Login request denied" + "message": "登入要求被拒絕" }, "allLoginRequestsDenied": { - "message": "All login requests denied" + "message": "所有登入要求被拒絕" }, "loginRequestApproved": { - "message": "Login request approved" + "message": "登入要求已批准" }, "removeOrgUserNoMasterPasswordTitle": { - "message": "Account does not have master password" + "message": "帳戶沒有主密碼" }, "removeOrgUserNoMasterPasswordDesc": { "message": "Removing $USER$ without setting a master password for them may restrict access to their full account. Are you sure you want to continue?", @@ -6981,13 +6981,13 @@ } }, "noMasterPassword": { - "message": "No master password" + "message": "沒有主密碼" }, "removeMembersWithoutMasterPasswordWarning": { "message": "Removing members who do not have master passwords without setting one for them may restrict access to their full account." }, "approvedAuthRequest": { - "message": "Approved device for $ID$.", + "message": "核准了 $ID$ 的裝置。", "placeholders": { "id": { "content": "$1", @@ -6996,7 +6996,7 @@ } }, "rejectedAuthRequest": { - "message": "Denied device for $ID$.", + "message": "拒絕了 $ID$ 的裝置。", "placeholders": { "id": { "content": "$1", @@ -7005,7 +7005,7 @@ } }, "requestedDeviceApproval": { - "message": "Requested device approval." + "message": "要求了裝置核准。" }, "startYour7DayFreeTrialOfBitwardenFor": { "message": "Start your 7-Day free trial of Bitwarden for $ORG$", @@ -7023,39 +7023,39 @@ "message": "Selected region flag" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "已成功建立帳戶!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "已要求管理員核准" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "您的要求已傳送給您的管理員" }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "批准後將通知您。" }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "登入時遇到困難?" }, "loginApproved": { - "message": "Login approved" + "message": "登入已批准" }, "userEmailMissing": { "message": "User email missing" }, "deviceTrusted": { - "message": "Device trusted" + "message": "裝置已信任" }, "sendsNoItemsTitle": { - "message": "No active Sends", + "message": "沒有可用的 Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", + "message": "使用 Send 可以與任何人安全地共用加密資訊。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inviteUsers": { - "message": "Invite Users" + "message": "邀請使用者" }, "secretsManagerForPlan": { "message": "Secrets Manager for $PLAN$", @@ -7070,7 +7070,7 @@ "message": "提供工程與 DevOps 團隊一套在軟體開發生命週期中,管理秘密資訊的功能。" }, "free2PersonOrganization": { - "message": "2 人組織免費版" + "message": "免費的 2 人組織" }, "unlimitedSecrets": { "message": "無限秘密資訊" @@ -7088,7 +7088,7 @@ } }, "serviceAccountsIncluded": { - "message": "包含 $COUNT$ 組服務帳號", + "message": "包含 $COUNT$ 組服務帳戶", "placeholders": { "count": { "content": "$1", @@ -7112,7 +7112,7 @@ "message": "將 Secrets Manager 加入您的升級方案,來記錄維持存取於先前方案中保存的秘密資訊。" }, "additionalServiceAccounts": { - "message": "新增服務帳號" + "message": "額外服務帳戶" }, "includedServiceAccounts": { "message": "Your plan comes with $COUNT$ service accounts.", @@ -7145,19 +7145,19 @@ "message": "免費組織" }, "limitServiceAccounts": { - "message": "限制服務帳號數量(選填)" + "message": "限制服務帳戶數量(選填)" }, "limitServiceAccountsDesc": { - "message": "為您的服務帳號數量設定限制。達到此限制後,就無法再新增服務帳號資訊。" + "message": "為您的服務帳戶數量設定限制。達到此限制後,就無法再新增服務帳戶。" }, "serviceAccountLimit": { - "message": "服務帳號數量限制(選填)" + "message": "服務帳戶數量限制(選填)" }, "maxServiceAccountCost": { - "message": "可能的最大服務帳號成本" + "message": "可能的最大服務帳戶成本" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "已登入!" }, "smBetaEndedDesc": { "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", @@ -7173,12 +7173,12 @@ } }, "betaEnding": { - "message": "Beta Ending" + "message": "Beta 版即將結束" }, "beta": { - "message": "Beta" + "message": "Beta 版" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "已經有帳戶?" } } From 9f00149cc36d44786b96d4cd50bf97cd2225a252 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:18:47 +0000 Subject: [PATCH 130/135] Autosync the updated translations (#6289) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 4 +- apps/desktop/src/locales/ar/messages.json | 4 +- apps/desktop/src/locales/az/messages.json | 6 +- apps/desktop/src/locales/be/messages.json | 4 +- apps/desktop/src/locales/bg/messages.json | 6 +- apps/desktop/src/locales/bn/messages.json | 4 +- apps/desktop/src/locales/bs/messages.json | 4 +- apps/desktop/src/locales/ca/messages.json | 6 +- apps/desktop/src/locales/cs/messages.json | 6 +- apps/desktop/src/locales/cy/messages.json | 4 +- apps/desktop/src/locales/da/messages.json | 4 +- apps/desktop/src/locales/de/messages.json | 4 +- apps/desktop/src/locales/el/messages.json | 4 +- apps/desktop/src/locales/en_GB/messages.json | 4 +- apps/desktop/src/locales/en_IN/messages.json | 4 +- apps/desktop/src/locales/eo/messages.json | 4 +- apps/desktop/src/locales/es/messages.json | 4 +- apps/desktop/src/locales/et/messages.json | 4 +- apps/desktop/src/locales/eu/messages.json | 4 +- apps/desktop/src/locales/fa/messages.json | 4 +- apps/desktop/src/locales/fi/messages.json | 6 +- apps/desktop/src/locales/fil/messages.json | 4 +- apps/desktop/src/locales/fr/messages.json | 6 +- apps/desktop/src/locales/gl/messages.json | 4 +- apps/desktop/src/locales/he/messages.json | 4 +- apps/desktop/src/locales/hi/messages.json | 4 +- apps/desktop/src/locales/hr/messages.json | 114 +++++++++---------- apps/desktop/src/locales/hu/messages.json | 6 +- apps/desktop/src/locales/id/messages.json | 4 +- apps/desktop/src/locales/it/messages.json | 4 +- apps/desktop/src/locales/ja/messages.json | 6 +- apps/desktop/src/locales/ka/messages.json | 4 +- apps/desktop/src/locales/km/messages.json | 4 +- apps/desktop/src/locales/kn/messages.json | 4 +- apps/desktop/src/locales/ko/messages.json | 4 +- apps/desktop/src/locales/lt/messages.json | 4 +- apps/desktop/src/locales/lv/messages.json | 28 ++--- apps/desktop/src/locales/me/messages.json | 4 +- apps/desktop/src/locales/ml/messages.json | 4 +- apps/desktop/src/locales/mr/messages.json | 4 +- apps/desktop/src/locales/my/messages.json | 4 +- apps/desktop/src/locales/nb/messages.json | 4 +- apps/desktop/src/locales/ne/messages.json | 4 +- apps/desktop/src/locales/nl/messages.json | 4 +- apps/desktop/src/locales/nn/messages.json | 4 +- apps/desktop/src/locales/or/messages.json | 4 +- apps/desktop/src/locales/pl/messages.json | 6 +- apps/desktop/src/locales/pt_BR/messages.json | 4 +- apps/desktop/src/locales/pt_PT/messages.json | 6 +- apps/desktop/src/locales/ro/messages.json | 4 +- apps/desktop/src/locales/ru/messages.json | 6 +- apps/desktop/src/locales/si/messages.json | 4 +- apps/desktop/src/locales/sk/messages.json | 6 +- apps/desktop/src/locales/sl/messages.json | 4 +- apps/desktop/src/locales/sr/messages.json | 6 +- apps/desktop/src/locales/sv/messages.json | 4 +- apps/desktop/src/locales/te/messages.json | 4 +- apps/desktop/src/locales/th/messages.json | 4 +- apps/desktop/src/locales/tr/messages.json | 6 +- apps/desktop/src/locales/uk/messages.json | 4 +- apps/desktop/src/locales/vi/messages.json | 4 +- apps/desktop/src/locales/zh_CN/messages.json | 16 +-- apps/desktop/src/locales/zh_TW/messages.json | 12 +- 63 files changed, 217 insertions(+), 217 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 7ef63a8aab2..dd0843dc188 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Stuur tans persoonlike kluis uit" }, - "exportingPersonalVaultDescription": { - "message": "Slegs die persoonlike kluisitems wat met $EMAIL$ verbind word, word uitgestuur. Organisasiekluisitems word nie ingesluit nie.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index ceefd1ecc26..738634afb93 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "تصدير الخزنة الشخصية" }, - "exportingPersonalVaultDescription": { - "message": "سيتم تصدير فقط عناصر الخزنة الشخصية المرتبطة بـ $EMAIL$. لن يتم إدراج عناصر خزنة المؤسسة.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 8d0f0211a4c..3d842ff3843 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Şəxsi anbarın ixracı" }, - "exportingPersonalVaultDescription": { - "message": "Yalnız $EMAIL$ ilə əlaqəli şəxsi anbar elementləri ixrac ediləcək. Təşkilat anbar elementləri daxil edilmir.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "öz-özünə sahiblik edən" }, "accessDenied": { "message": "Müraciət rədd edildi. Bu səhifəyə baxmaq üçün icazəniz yoxdur." diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 666dac70c24..3148dcb2b42 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Экспартаванне асабістага сховішча" }, - "exportingPersonalVaultDescription": { - "message": "Будуць экспартаваны толькі асабістыя элементы сховішча, якія звязаны з $EMAIL$. Элементы сховішча арганізацыі не будуць уключаны.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index bd6552824a5..6266883d29b 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Изнасяне на личния трезор" }, - "exportingPersonalVaultDescription": { - "message": "Ще бъдат изнесени само записите от личния трезор свързан с $EMAIL$. Записите в трезора на организацията няма да бъдат включени.", + "exportingIndividualVaultDescription": { + "message": "Ще бъдат изнесени само отделните записи в трезора, които са свързани с $EMAIL$. Записите от трезора на организацията няма да бъдат включени. Ще бъде изнесена само информацията за записите от трезора, а свързаните прикачени елементи няма да бъдат включени.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "собствен хостинг" }, "accessDenied": { "message": "Достъпът е отказан. Нямате право за преглед на тази страница." diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 0297dfc157c..d94904b486e 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 0d5ed75caf9..dbe4af97e66 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index ab40caa8431..365ca0dda4d 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "S'està exportant la caixa forta personal" }, - "exportingPersonalVaultDescription": { - "message": "Només s'exportaran els elements personals de la caixa forta associats a $EMAIL$. Els elements de la caixa forta de l'organització no s'inclouran.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "autoallotjat" }, "accessDenied": { "message": "Accés denegat. No teniu permís per veure aquesta pàgina." diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 3d90680ae0e..36bd494bbb5 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exportování osobního trezoru" }, - "exportingPersonalVaultDescription": { - "message": "Budou exportovány jen osobní položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "vlastní hosting" }, "accessDenied": { "message": "Přístup byl odepřen. Nemáte oprávnění k zobrazení této stránky." diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 6d569a89554..1eaa44b7109 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 0f3a5603b7e..d1d91b32676 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Eksporterer individuel boks" }, - "exportingPersonalVaultDescription": { - "message": "Kun de individuelle boksemner tilknyttet $EMAIL$ eksporteres. Organisationsboksemner medtages ikke.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 889446eff62..b946a3f5f37 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Persönlichen Tresor exportieren" }, - "exportingPersonalVaultDescription": { - "message": "Nur die persönlichen Tresor-Einträge, die mit $EMAIL$ verbunden sind, werden exportiert. Tresor-Einträge der Organisation werden nicht berücksichtigt.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 9e2c135b873..8bab72d6219 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Εξαγωγή Προσωπικού Vault" }, - "exportingPersonalVaultDescription": { - "message": "Θα εξαχθούν μόνο τα προσωπικά αντικείμενα Vault που σχετίζονται με το $EMAIL$. Τα αντικείμενα Vault οργανισμού δεν θα συμπεριληφθούν.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 923a8e143b9..d2b0cc4dae2 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organisation vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 19c80a97d3b..a641a0a8b60 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 0da8a023615..77199adbb81 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index aaaa54a35a4..ca03ed49e98 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exportando caja fuerte personal" }, - "exportingPersonalVaultDescription": { - "message": "Solo se exportarán los elementos de la caja fuerte personal asociados con $EMAIL$. Los elementos de la bóveda de la organización no se incluirán.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 603f695d5b3..65252b69c92 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Personaalse hoidla eksportimine" }, - "exportingPersonalVaultDescription": { - "message": "Ainult personaalsed $EMAIL$ alla kuuluvad kirjed eksportidakse. Organisatsiooni kirjeid ei ekspordita.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 517b56a07f8..7cea45c7e4e 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Kutxa gotor pertsonala esportatzen" }, - "exportingPersonalVaultDescription": { - "message": "$EMAIL$-ekin lotutako kutxa gotor pertsonaleko elementuak bakarrik esportatuko dira. Erakundeko kutxa gotorraren elementuak ez dira sartuko.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index bf7f8b25329..bb429f07742 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "برون ریزی گاو‌صندوق شخصی" }, - "exportingPersonalVaultDescription": { - "message": "فقط موارد گاو‌صندوق شخصی مرتبط با $EMAIL$ برون ریزی خواهد شد. موارد گاو‌صندوق سازمان شامل نخواهد شد.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 76bd8d322d4..0e28e0217eb 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Henkilökohtaisen holvin vienti" }, - "exportingPersonalVaultDescription": { - "message": "Vain tunnukseen $EMAIL$ liitetyt henkilökohtaisen holvin kohteet viedään. Organisaation kohteet eivät sisälly tähän.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "itse ylläpidetty" }, "accessDenied": { "message": "Pääsy estetty. Sinulla ei ole oikeutta avata sivua." diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index c89830c0d8f..69a63c46c1a 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Pag-export ng indibidwal na vault" }, - "exportingPersonalVaultDescription": { - "message": "Tanging ang mga indibidwal na vault item na nauugnay sa $EMAIL$ ang i-export. Hindi isasama ang mga vault item ng organisasyon.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 776e518fea8..0e817f63285 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Export du coffre personnel" }, - "exportingPersonalVaultDescription": { - "message": "Seuls les éléments individuels du coffre associés à $EMAIL$ seront exportés. Les éléments du coffre de l'organisation ne seront pas inclus.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "auto-hébergé" }, "accessDenied": { "message": "Accès refusé. Vous n'avez pas l'autorisation de voir cette page." diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 6d569a89554..1eaa44b7109 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 6fd77738b06..37d13549d77 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "הכספת האישית מיוצאת" }, - "exportingPersonalVaultDescription": { - "message": "רק פריטי הכספת האישית שמשויכת עם $EMAIL$ ייוצאו. פריטי הכספת הארגוניים לא יהיו חלק מהייצוא.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index b19b9abd72e..d3c58ec6e8f 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index ab08f7d5de9..e30758aaa13 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -771,7 +771,7 @@ "message": "Kontaktiraj nas" }, "helpAndFeedback": { - "message": "Help and feedback" + "message": "Pomoć i podrška" }, "getHelp": { "message": "Potraži pomoć" @@ -1078,7 +1078,7 @@ "message": "1 GB šifriranog prostora za pohranu podataka." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Mogućnosti za prijavu u dva koraka kao što su YubiKey i Duo." }, "premiumSignUpReports": { "message": "Higijenu lozinki, zdravlje računa i izvještaje o krađi podatak radi zaštite svojeg trezora." @@ -1393,7 +1393,7 @@ "message": "Otključaj koristeći Windows Hello" }, "additionalWindowsHelloSettings": { - "message": "Additional Windows Hello settings" + "message": "Dodatne Windows Hello postavke" }, "windowsHelloConsentMessage": { "message": "Otključaj trezor." @@ -1402,7 +1402,7 @@ "message": "Otključaj koristeći Touch ID" }, "additionalTouchIdSettings": { - "message": "Additional Touch ID settings" + "message": "Dodatne Touch ID postavke" }, "touchIdConsentMessage": { "message": "Otključaj trezor" @@ -1414,10 +1414,10 @@ "message": "Zahtijevaj Touch ID pri pokretanju" }, "requirePasswordOnStart": { - "message": "Require password or PIN on app start" + "message": "Zahtijevaj lozinku ili PIN pri pokretanju" }, "recommendedForSecurity": { - "message": "Recommended for security." + "message": "Preporučeno za sigurnost." }, "lockWithMasterPassOnRestart": { "message": "Zaključaj glavnom lozinkom kod svakog pokretanja" @@ -1487,13 +1487,13 @@ "message": "Nakon isteka trezora" }, "vaultTimeoutActionLockDesc": { - "message": "Potrebno je ponovno unijeti glavnu lozinku ili na drugi način otključati za pristup tvom trezoru." + "message": "Za pristup tvom trezoru, potrebno je ponovno unijeti glavnu lozinku ili ga na drugi način otključati." }, "vaultTimeoutActionLogOutDesc": { - "message": "Potrebno je ponovno unijeti korisničko ime i glavnu lozinku za pristup tvom trezoru." + "message": "Za pristup tvom trezoru, potrebno je ponovno unijeti korisničko ime i glavnu lozinku." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Za promjenu vremena isteka trezora, odredi način otključavanja." }, "lock": { "message": "Zaključaj", @@ -1525,7 +1525,7 @@ "message": "Odjava će ukloniti pristup tvojem trezoru i zahtijeva mrežnu potvrdu identiteta nakon isteka vremenske neaktivnosti. Sigurno želiš koristiti ovu postavku?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Potvrda akcije vremenske neaktivnosti" + "message": "Potvrda radnje nakon vremenske neaktivnosti" }, "enterpriseSingleSignOn": { "message": "Jedinstvena prijava na razini tvrtke (SSO)" @@ -1537,7 +1537,7 @@ "message": "Za dovršetak jedinstvene prijave na razini tvrtke (SSO), postavi glavnu lozinku za pristup i zaštitu tvog trezora." }, "currentMasterPass": { - "message": "Current master password" + "message": "Trenutna glavna lozinka" }, "newMasterPass": { "message": "Nova glavna lozinka" @@ -1856,7 +1856,7 @@ "message": "Tvoju glavnu lozinku je nedavno promijenio administrator tvoje organizacije. Za pristup trezoru, potrebno je ažurirati glavnu lozinku, što će te odjaviti iz trenutne sesije, te ćeš se morati ponovno prijaviti. Aktivne sesije na drugim uređajima mogu ostati aktivne još sat vremena." }, "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": "Tvoja glavna lozinka ne zadovoljava pravila ove organizacije. Za pristup trezoru moraš odmah ažurirati svoju glavnu lozinku. Ako nastaviš, odjaviti ćeš se iz trenutne sesije te ćeš se morati ponovno prijaviti. Aktivne sesije na drugim uređajima mogu ostati aktivne do jedan sat." }, "hours": { "message": "sat(i)" @@ -1878,7 +1878,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "Pravilo tvoje organizacije utječe na istek trezora. Najveće dozvoljeno vrijeme isteka je $HOURS$:$MINUTES$ h. Tvoja radnja nakon isteka trezora je: $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -1895,7 +1895,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "Pravilo tvoje organizacije podesilo je radnju nakon isteka trezora na: $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Izvoz osobnog trezora u tijeku" }, - "exportingPersonalVaultDescription": { - "message": "Izvest će se samo stavke osobnog trezora povezanog s $EMAIL$. Stavke organizacijskog trezora neće biti uključene.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2110,7 +2110,7 @@ "message": "Prijava drugim uređajem" }, "loginInitiated": { - "message": "Login initiated" + "message": "Prijava pokrenuta" }, "notificationSentDevice": { "message": "Obavijest je poslana na tvoj uređaj." @@ -2244,41 +2244,41 @@ } }, "windowsBiometricUpdateWarning": { - "message": "Bitwarden recommends updating your biometric settings to require your master password (or PIN) on the first unlock. Would you like to update your settings now?" + "message": "Bitwarden preporučuje ažuriranje tvojih biometrijskih postavki kako bi se pri prvom otključavanju zahtijevala glavna lozinka (ili PIN). Želiš li sada ažurirati svoje postavke?" }, "windowsBiometricUpdateWarningTitle": { - "message": "Recommended Settings Update" + "message": "Preporučeno ažuriranje postavki" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Potrebno je odobriti uređaj. Odaberi metodu odobravanja:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Zapamti ovaj uređaj" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Odznači ako koristiš javni uređaj" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Odobri drugim uređajem" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Zatraži odobrenje administratora" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Odobri glavnom lozinkom" }, "region": { - "message": "Region" + "message": "Regija" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Potreban je identifikator organizacije." }, "eu": { "message": "EU", "description": "European Union" }, "loggingInOn": { - "message": "Logging in on" + "message": "Prijava na" }, "usDomain": { "message": "bitwarden.com" @@ -2287,46 +2287,46 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "vlastiti poslužitelj" }, "accessDenied": { - "message": "Access denied. You do not have permission to view this page." + "message": "Pristup odbijen. Nemaš prava vidjeti ovu stranicu." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Račun je uspješno stvoren!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Zatraženo odobrenje administratora" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Tvoj zahtjev je poslan administratoru." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Dobiti ćeš obavijest kada bude odobreno." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Problem s prijavom?" }, "loginApproved": { - "message": "Login approved" + "message": "Prijava odobrena" }, "userEmailMissing": { - "message": "User email missing" + "message": "Nedostaje e-pošta korisnika" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Uređaj pouzdan" }, "inputRequired": { - "message": "Input is required." + "message": "Potreban je unos." }, "required": { - "message": "required" + "message": "obavezno" }, "search": { - "message": "Search" + "message": "Traži" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Unos mora sadržavati najmanje $COUNT$ znakova.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Unos ne smije imati više od $COUNT$ znakova.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Ovi znakovi nisu dozvoljeni: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Unos mora biti najmanje $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Unos ne smije biti više od $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "Jedna ili više adresa e-pošte nije valjana" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Unos ne smije biti prazan.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Nije unesena adresa e-pošte." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ polje/a treba tvoju pažnju.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Odaberi --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Upiši za filtriranje --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Dohvaćanje opcija..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Nije pronađena niti jedna stavka" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Očisti sve" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ još $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "Podizbornik" } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 7df69e64b83..63f52bcebf8 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Személyes széf exportálása" }, - "exportingPersonalVaultDescription": { - "message": "Csak $EMAIL$ email címmel társított személyes széf elemek kerülnek exportálásra. Ebbe nem kerülnek be a szervezeti széf elemek.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "saját üzemeltetésű" }, "accessDenied": { "message": "A hozzáférés megtagadásra került. Nincs jogosultság az oldal megtekintésére." diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 5dca15ddee2..728e5581722 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 3b4c20e0f00..7bff6e03ab7 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Esportando cassaforte individuale" }, - "exportingPersonalVaultDescription": { - "message": "Solo gli elementi della cassaforte personale associati a $EMAIL$ saranno esportati. Gli elementi della cassaforte dell'organizzazione non saranno inclusi.", + "exportingIndividualVaultDescription": { + "message": "Solo gli elementi della cassaforte personale associati a $EMAIL$ saranno esportati. Gli elementi della cassaforte dell'organizzazione non saranno inclusi. Solo le informazioni sugli elementi della cassaforte saranno esportate e non includeranno gli allegati.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index a4f830256fa..3efe9df6678 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "個人保管庫のエクスポート" }, - "exportingPersonalVaultDescription": { - "message": "$EMAIL$ に関連付けられた個人用保管庫アイテムのみがエクスポートされます。組織用保管庫アイテムは含まれません。", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "自己ホスト型" }, "accessDenied": { "message": "アクセスが拒否されました。このページを表示する権限がありません。" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 6d569a89554..1eaa44b7109 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 6d569a89554..1eaa44b7109 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 5ba16861ece..cc595c6efeb 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index afe34afb3f3..6433157727d 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "개인 보관함을 내보내는 중" }, - "exportingPersonalVaultDescription": { - "message": "$EMAIL$ 이메일과 관련이 있는 개인 보관함 항목만 내보내집니다. 조직의 보관함 항목은 포함되지 않습니다.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index c15d273916d..033ba1910fe 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index b82788f8d30..d6f1c0d6fb3 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -646,21 +646,21 @@ "message": "YubiKey OTP drošības atslēga" }, "yubiKeyDesc": { - "message": "Izmanto YubiKey, lai piekļūtu savam kontam! Darbojas ar YubiKey 4, 4 Nano, 4C un NEO ierīcēm." + "message": "Ir izmantojams YubiKey, lai piekļūtu savam kontam. Darbojas ar YubiKey 4, 4 Nano, 4C un NEO ierīcēm." }, "duoDesc": { - "message": "Apstiprini ar Duo Security, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu!", + "message": "Ar Duo Security apliecināšanu var veikt ar Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Apstiprini ar Duo Security savā apvienībā, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu!", + "message": "Apliecināšana ar savas apvienības Duo Security, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Izmantot jebkuru WebAuthn atbalstošu drošības atslēgu, lai piekļūtu kontam." + "message": "Ir izmantojama jebkura WebAuthn atbalstošu drošības atslēgu, lai piekļūtu kontam." }, "emailTitle": { "message": "E-pasts" @@ -675,7 +675,7 @@ "message": "Šim kontam ir iespējota divpakāpju pieteikšanās, bet šajā ierīcē netiek atbalstīts neviens no uzstādītajiem divpakāpju pārbaudes nodrošinātājiem." }, "noTwoStepProviders2": { - "message": "Lūgums pievienot papildus nodrošinātājus, kas tiek labāk atbalstīti dažādās ierīcēs (piemēram, autentificētāja lietotne)." + "message": "Lūgums pievienot papildu nodrošinātājus, kas tiek labāk atbalstīti dažādās ierīcēs (piemēram, autentificētāja lietotne)." }, "twoStepOptions": { "message": "Divpakāpju pieteikšanās iespējas" @@ -1191,7 +1191,7 @@ "message": "Izslēgt Bitwarden" }, "valueCopied": { - "message": "$VALUE$ ievietota starpliktuvē", + "message": "$VALUE$ ir starpliktuvē", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -1423,13 +1423,13 @@ "message": "Aizslēgt ar galveno paroli pēc pārsāknēšanas" }, "deleteAccount": { - "message": "Dzēst kontu" + "message": "Izdzēst kontu" }, "deleteAccountDesc": { "message": "Turpināt zemāk, lai izdzēstu kontu un visu glabātavas sasturu." }, "deleteAccountWarning": { - "message": "Konta dzēšana ir paliekoša. To nevar atsaukt." + "message": "Konta izdzēšana ir neatgriezeniska. To nevar atsaukt." }, "accountDeleted": { "message": "Konts tika izdzēsts" @@ -1453,7 +1453,7 @@ "message": "Ir jāizvēlas vismaz viens krājums." }, "premiumUpdated": { - "message": "Tu esi pārgājis uz Premium." + "message": "Konts tika uzlabots uz Premium." }, "restore": { "message": "Atjaunot" @@ -1588,7 +1588,7 @@ "message": "Jaunā galvenā parole neatbilst nosacījumu prasībām." }, "acceptPolicies": { - "message": "Atzīmējot šo rūtiņu, Tu piekrīti sekojošajam:" + "message": "Ar šīs rūtiņas atzīmēšanu tiek piekrists sekojošajam:" }, "acceptPoliciesRequired": { "message": "Nav apstiprināti izmantošanas noteikumi un privātuma nosacījumi." @@ -1621,7 +1621,7 @@ "message": "Pieprasīt apstiprinājumu sasaistīšanai ar pārlūku" }, "enableBrowserIntegrationFingerprintDesc": { - "message": "Iespējo papildus drošības slāni, pieprasot atpazīšanas vārdkopas pārbaudi, kad tiek izveidota saikne starp darbvirsmu un pārlūku. Kad iespējots, ir nepieciešama lietotāja mijiedarbīga un apstiprināšana katru reizi, kad tiek izveidots savienojums." + "message": "Iespējo papildu drošības slāni, pieprasot atpazīšanas vārdkopas pārbaudi, kad tiek izveidota saikne starp darbvirsmu un pārlūku. Ir nepieciešama lietotāja darbība un apstiprināšana katru reizi, kad tiek izveidots savienojums." }, "approve": { "message": "Apstiprināt" @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Izdod personīgo glabātavu" }, - "exportingPersonalVaultDescription": { - "message": "Tiks izdoti tikai personīgie glabātavas vienumi, kas ir saistīti ar $EMAIL$. Apvienības glabātavas vienumi netiks iekļauti.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "pašizvietots" }, "accessDenied": { "message": "Piekļuve liegta. Nav nepieciešamo atļauju, lai skatītu šo lapu." diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 9a72e54c6bf..375a8b2e9aa 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index b133b150885..eb7ca9d8d29 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 6d569a89554..1eaa44b7109 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 294124b241b..d047602948c 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index a84f7539b88..cb92f718040 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Eksporter personlig hvelv" }, - "exportingPersonalVaultDescription": { - "message": "Bare de personlige hvelv-elementene som er knyttet til $EMAIL$ vil bli eksportert. Organisasjonshvelvets elementer vil ikke bli inkludert.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 369bdaef7b9..2c81328ec6e 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 13f6df5faf6..8c7058c2cca 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Persoonlijke kluis exporteren" }, - "exportingPersonalVaultDescription": { - "message": "Exporteert alleen de persoonlijke kluis-items gerelateerd aan $EMAIL$. Geen kluis-items van de organisatie.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index cd30ef54fdd..4a5dc712873 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 355595090e0..bdaf9990917 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 6ddad947f21..d730a0e9902 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Eksportowanie osobistego sejfu" }, - "exportingPersonalVaultDescription": { - "message": "Tylko osobiste elementy sejfu powiązane z adresem $EMAIL$ zostaną wyeksportowane. Elementy sejfu należące do organizacji nie będą uwzględnione.", + "exportingIndividualVaultDescription": { + "message": "Z sejfu zostaną wyeksportowane tylko elementy powiązane z $EMAIL$. Elementy z sejfu organizacji nie będą uwzględnione. Tylko informacje o elemencie zostaną wyeksportowane i nie będą zawierać powiązanych załączników.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "samodzielnie hostowany" }, "accessDenied": { "message": "Odmowa dostępu. Nie masz uprawnień do przeglądania tej strony." diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 8b4eb7dcc35..eaf3663b8f0 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exportação do Cofre Pessoal" }, - "exportingPersonalVaultDescription": { - "message": "Apenas os itens pessoais do cofre associados com $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 49257f09d83..78298e1c536 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "A exportar o cofre pessoal" }, - "exportingPersonalVaultDescription": { - "message": "Apenas os itens do cofre pessoal associado a $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos.", + "exportingIndividualVaultDescription": { + "message": "Apenas os itens de cofre individuais associados a $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos. Apenas serão exportadas as informações do item do cofre e não serão incluídos os anexos associados.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "auto-hospedado" }, "accessDenied": { "message": "Acesso negado. Não tem permissão para visualizar esta página." diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 139e0813cc8..28cc9783f70 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exportul seifului individual" }, - "exportingPersonalVaultDescription": { - "message": "Numai articolele de seif individuale asociate cu $EMAIL$ vor fi exportate. Articolele de seif ale organizației nu vor fi incluse.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index b16eb66ac89..f33c9e95705 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Экспорт личного хранилища" }, - "exportingPersonalVaultDescription": { - "message": "Будут экспортированы только личные элементы хранилища, связанные с $EMAIL$. Элементы хранилища организации включены не будут.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "собственный хостинг" }, "accessDenied": { "message": "Доступ запрещен. У вас нет разрешения на просмотр этой страницы." diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 9d8a1428da0..8f50283ba62 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 7277dfca0b8..87fdb105639 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exportovanie osobného trezora" }, - "exportingPersonalVaultDescription": { - "message": "Exportované budú iba položy osobného trezora spojené s $EMAIL$. Položky trezora organizácie nebudú zahrnuté.", + "exportingIndividualVaultDescription": { + "message": "Exportované budú iba položky súvisiace s $EMAIL$. Položky z trezora organizácie nebudú zahrnuté v exporte. Export bude obsahovať iba informácie z položiek v trezore, súvisiace prílohy nebudú súčasťou exportu.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "vlastný hosting" }, "accessDenied": { "message": "Prístup zamietnutý. Nemáte oprávnenie na zobrazenie tejto stránky." diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index f12e6f5e855..3926e5a7aca 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 428cabcb9ee..0e0a7c397e1 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Извоз личног сефа" }, - "exportingPersonalVaultDescription": { - "message": "Само предмети личног сефа повезани са $EMAIL$ биће извезени. Ставке организационог сефа неће бити укључене.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "личан хостинг" }, "accessDenied": { "message": "Одбијен приступ. Немате дозволу да видите ову страницу." diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 012566d49fc..70146e2f149 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporterar individuellt valv" }, - "exportingPersonalVaultDescription": { - "message": "Endast de personliga valvobjekt som är associerade med $EMAIL$ kommer att exporteras. Organisationens valvobjekt kommer inte att inkluderas.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 6d569a89554..1eaa44b7109 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index f35de19ff34..e9a98e21258 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index ae8cc13e1bd..7e735b4cd5b 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Kişisel kasa dışa aktarılıyor" }, - "exportingPersonalVaultDescription": { - "message": "Yalnızca $EMAIL$ ile ilişkili kişisel kasadaki kayıtlar dışa aktarılacaktır. Kuruluş kasasındaki kayıtlar dahil edilmeyecektir.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "şirket içinde barındırılan" }, "accessDenied": { "message": "Erişim engellendi. Bu sayfayı görüntüleme iznine sahip değilsiniz." diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index cf119fc5b07..fded60fe507 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Експортування особистого сховища" }, - "exportingPersonalVaultDescription": { - "message": "Будуть експортовані лише записи особистого сховища, пов'язані з $EMAIL$. Записи сховища організації не буде включено.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 51577198cf9..96498d741ae 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index a8da710ca61..a0c1e85b730 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -509,7 +509,7 @@ "message": "主密码是访问密码库的唯一密码。它非常重要,请您不要忘记。忘记主密码后,我们无法为您恢复或重置它。" }, "masterPassHintDesc": { - "message": "主密码提示可以在你忘记密码时帮你回忆。" + "message": "主密码提示可以在您忘记密码时帮您回忆起来。" }, "reTypeMasterPass": { "message": "再次输入主密码" @@ -1429,7 +1429,7 @@ "message": "接下来的操作会删除您的账户和所有密码库数据。" }, "deleteAccountWarning": { - "message": "删除账户是永久性的。不能被撤消。" + "message": "删除账户是永久性操作,无法撤销!" }, "accountDeleted": { "message": "账户已删除" @@ -1654,7 +1654,7 @@ "message": "需要先在桌面应用程序的设置中设置生物识别,才能使用浏览器中的生物识别。" }, "personalOwnershipSubmitError": { - "message": "由于企业策略,您被限制为保存项目到您的个人密码库。将所有权选项更改为组织,然后从可用的集合中选择。" + "message": "由于某个企业策略,您不能将项目保存到您的个人密码库。将所有权选项更改为组织,并从可用的集合中选择。" }, "hintEqualsPassword": { "message": "您的密码提示不能与您的密码相同。" @@ -1795,7 +1795,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "由于企业策略,您只能删除现有的 Send。", + "message": "由于某个企业策略,您只能删除现有的 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copyLink": { @@ -1838,7 +1838,7 @@ "message": "您必须验证您的电子邮件才能使用此功能。" }, "passwordPrompt": { - "message": "重新询问主密码" + "message": "主密码重新提示" }, "passwordConfirmation": { "message": "确认主密码" @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "正在导出个人密码库" }, - "exportingPersonalVaultDescription": { - "message": "仅会导出与 $EMAIL$ 关联的个人密码库项目。组织密码库的项目不会导出。", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2287,7 +2287,7 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "自托管" }, "accessDenied": { "message": "访问被拒绝。您没有权限查看此页面。" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 27d9993f543..32a511beab1 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -1207,10 +1207,10 @@ "message": "視窗" }, "checkPassword": { - "message": "檢查密碼是否已外洩。" + "message": "檢查密碼是否已暴露。" }, "passwordExposed": { - "message": "此密碼已外洩了 $VALUE$ 次,應立即變更密碼。", + "message": "此密碼在資料外洩事件中被暴露了 $VALUE$ 次,應立即變更它。", "placeholders": { "value": { "content": "$1", @@ -1984,8 +1984,8 @@ "exportingPersonalVaultTitle": { "message": "正匯出個人密碼庫" }, - "exportingPersonalVaultDescription": { - "message": "只會匯出與 $EMAIL$ 關聯的個人密碼庫。組織密碼庫的項目不包含在內。", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2217,7 +2217,7 @@ "message": "已暴露的主密碼" }, "exposedMasterPasswordDesc": { - "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + "message": "在資料外洩事件中找到了密碼。我們建議您使用一個獨特的密碼來保護您的帳戶,您確定要使用已暴露的密碼嗎?" }, "weakAndExposedMasterPassword": { "message": "強度不足且已暴露的主密碼" @@ -2293,7 +2293,7 @@ "message": "拒絕存取。您沒有檢視此頁面的權限。" }, "accountSuccessfullyCreated": { - "message": "成功建立帳號!" + "message": "已成功建立帳戶!" }, "adminApprovalRequested": { "message": "需要管理員批准" From 90d465509274252f5c509acd697a86070f9424ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:20:37 +0000 Subject: [PATCH 131/135] Autosync the updated translations (#6290) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 4 +- apps/browser/src/_locales/az/messages.json | 6 +- apps/browser/src/_locales/be/messages.json | 4 +- apps/browser/src/_locales/bg/messages.json | 6 +- apps/browser/src/_locales/bn/messages.json | 4 +- apps/browser/src/_locales/bs/messages.json | 4 +- apps/browser/src/_locales/ca/messages.json | 6 +- apps/browser/src/_locales/cs/messages.json | 6 +- apps/browser/src/_locales/cy/messages.json | 4 +- apps/browser/src/_locales/da/messages.json | 4 +- apps/browser/src/_locales/de/messages.json | 4 +- apps/browser/src/_locales/el/messages.json | 4 +- apps/browser/src/_locales/en_GB/messages.json | 4 +- apps/browser/src/_locales/en_IN/messages.json | 4 +- apps/browser/src/_locales/es/messages.json | 4 +- apps/browser/src/_locales/et/messages.json | 6 +- apps/browser/src/_locales/eu/messages.json | 4 +- apps/browser/src/_locales/fa/messages.json | 94 ++++++------- apps/browser/src/_locales/fi/messages.json | 6 +- apps/browser/src/_locales/fil/messages.json | 4 +- apps/browser/src/_locales/fr/messages.json | 6 +- apps/browser/src/_locales/gl/messages.json | 4 +- apps/browser/src/_locales/he/messages.json | 4 +- apps/browser/src/_locales/hi/messages.json | 4 +- apps/browser/src/_locales/hr/messages.json | 124 +++++++++--------- apps/browser/src/_locales/hu/messages.json | 6 +- apps/browser/src/_locales/id/messages.json | 4 +- apps/browser/src/_locales/it/messages.json | 4 +- apps/browser/src/_locales/ja/messages.json | 6 +- apps/browser/src/_locales/ka/messages.json | 4 +- apps/browser/src/_locales/km/messages.json | 4 +- apps/browser/src/_locales/kn/messages.json | 4 +- apps/browser/src/_locales/ko/messages.json | 4 +- apps/browser/src/_locales/lt/messages.json | 4 +- apps/browser/src/_locales/lv/messages.json | 22 ++-- apps/browser/src/_locales/ml/messages.json | 4 +- apps/browser/src/_locales/mr/messages.json | 4 +- apps/browser/src/_locales/my/messages.json | 4 +- apps/browser/src/_locales/nb/messages.json | 4 +- apps/browser/src/_locales/ne/messages.json | 4 +- apps/browser/src/_locales/nl/messages.json | 4 +- apps/browser/src/_locales/nn/messages.json | 4 +- apps/browser/src/_locales/or/messages.json | 4 +- apps/browser/src/_locales/pl/messages.json | 6 +- apps/browser/src/_locales/pt_BR/messages.json | 4 +- apps/browser/src/_locales/pt_PT/messages.json | 6 +- apps/browser/src/_locales/ro/messages.json | 4 +- apps/browser/src/_locales/ru/messages.json | 6 +- apps/browser/src/_locales/si/messages.json | 4 +- apps/browser/src/_locales/sk/messages.json | 6 +- apps/browser/src/_locales/sl/messages.json | 4 +- apps/browser/src/_locales/sr/messages.json | 6 +- apps/browser/src/_locales/sv/messages.json | 8 +- apps/browser/src/_locales/te/messages.json | 4 +- apps/browser/src/_locales/th/messages.json | 4 +- apps/browser/src/_locales/tr/messages.json | 6 +- apps/browser/src/_locales/uk/messages.json | 4 +- apps/browser/src/_locales/vi/messages.json | 4 +- apps/browser/src/_locales/zh_CN/messages.json | 24 ++-- apps/browser/src/_locales/zh_TW/messages.json | 30 ++--- 60 files changed, 274 insertions(+), 274 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 6bb0efb8fd3..d48c573d5b4 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "جاري تصدير الخزنة الشخصية" }, - "exportingPersonalVaultDescription": { - "message": "سيتم تصدير فقط عناصر الخزنة الشخصية المرتبطة بـ $EMAIL$. لن يتم إدراج عناصر خزنة المؤسسة.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 4ee8ab8edaf..774ca11ecf0 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Şəxsi anbarın ixracı" }, - "exportingPersonalVaultDescription": { - "message": "Yalnız $EMAIL$ ilə əlaqəli şəxsi anbar elementləri ixrac ediləcək. Təşkilat anbar elementləri daxil edilmir.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Server Versiyası" }, "selfHostedServer": { - "message": "self-hosted" + "message": "öz-özünə sahiblik edən" }, "thirdParty": { "message": "Üçüncü tərəf" diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index e57ea2eff54..ba72d949b90 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Экспартаванне асабістага сховішча" }, - "exportingPersonalVaultDescription": { - "message": "Будуць экспартаваны толькі асабістыя элементы сховішча, якія звязаны з $EMAIL$. Элементы сховішча арганізацыі не будуць уключаны.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 9e4d696193e..ceab700e46a 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Изнасяне на личния трезор" }, - "exportingPersonalVaultDescription": { - "message": "Ще бъдат изнесени само записите от личния трезор свързан с $EMAIL$. Записите в трезора на организацията няма да бъдат включени.", + "exportingIndividualVaultDescription": { + "message": "Ще бъдат изнесени само отделните записи в трезора, които са свързани с $EMAIL$. Записите от трезора на организацията няма да бъдат включени. Ще бъде изнесена само информацията за записите от трезора, а свързаните прикачени елементи няма да бъдат включени.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Версия на сървъра" }, "selfHostedServer": { - "message": "self-hosted" + "message": "собствен хостинг" }, "thirdParty": { "message": "Third-party" diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 261ceea180d..db189fbcf0b 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 5b27e7186d4..e10d4d29a8c 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 7a7edc3d896..08bef1a50c4 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "S'està exportant la caixa forta personal" }, - "exportingPersonalVaultDescription": { - "message": "Només s'exportaran els elements personals de la caixa forta associats a $EMAIL$. Els elements de la caixa forta de l'organització no s'inclouran.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Versió del servidor" }, "selfHostedServer": { - "message": "self-hosted" + "message": "autoallotjat" }, "thirdParty": { "message": "Tercers" diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 253aab484b7..3fccc2a3fd7 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exportování osobního trezoru" }, - "exportingPersonalVaultDescription": { - "message": "Budou exportovány jen osobní položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Verze serveru" }, "selfHostedServer": { - "message": "self-hosted" + "message": "vlastní hosting" }, "thirdParty": { "message": "Tretí strana" diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 7861a5e758b..2ce58e170a2 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index df94e9aab71..4552558cf7e 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Eksporterer personlig boks" }, - "exportingPersonalVaultDescription": { - "message": "Kun de personlige bokselementer tilknyttet $EMAIL$ vil blive eksporteret. Organisationsbokseelementer vil ikke være inkluderet.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 7dcdbc3c51f..fc11b77b157 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Persönlicher Tresor wird exportiert" }, - "exportingPersonalVaultDescription": { - "message": "Nur die einzelnen Tresor-Einträge, die mit $EMAIL$ verbunden sind, werden exportiert. Tresor-Einträge der Organisation werden nicht berücksichtigt.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 1e377c35d0d..fc1c858e7db 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Εξαγωγή Προσωπικού Vault" }, - "exportingPersonalVaultDescription": { - "message": "Θα εξαχθούν μόνο τα προσωπικά αντικείμενα Vault που σχετίζονται με το $EMAIL$ . Τα αντικείμενα Vault οργανισμού δεν θα συμπεριληφθούν.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 84fee7b77c3..3ea607f9381 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organisation vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 4bf280ce707..2da8f08deb2 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting Personal Vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the personal vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 50b721348c8..b428e097edd 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exportando caja fuerte personal" }, - "exportingPersonalVaultDescription": { - "message": "Solo se exportarán los elementos de la caja fuerte personal asociados a $EMAIL$. Los elementos de la caja fuerte de tu organización no se incluirán.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index f9a11297795..55d65dd0dd2 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Personaalse hoidla eksportimine" }, - "exportingPersonalVaultDescription": { - "message": "Ainult personaalsed $EMAIL$ alla kuuluvad kirjed eksportidakse. Organisatsiooni kirjeid ei ekspordita.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Serveri versioon" }, "selfHostedServer": { - "message": "self-hosted" + "message": "enda majutatud" }, "thirdParty": { "message": "Kolmanda osapoole" diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 987565d609d..ed4caa5d495 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Kutxa gotor pertsonala esportatzen" }, - "exportingPersonalVaultDescription": { - "message": "$EMAIL$-ekin lotutako kutxa gotor pertsonaleko elementuak bakarrik esportatuko dira. Erakundeko kutxa gotorraren elementuak ez dira sartuko.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index ebb75c9f893..8570b9c1c33 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -339,7 +339,7 @@ "message": "ساير" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "یک روش بازگشایی برای پایان زمان مجاز تنظیم کنید." }, "rateExtension": { "message": "به این افزونه امتیاز دهید" @@ -634,10 +634,10 @@ "message": "به‌روزرسانی" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "برای پر کردن خودکار گاوصندوق Bitwarden خود را باز کنید." }, "notificationUnlock": { - "message": "Unlock" + "message": "بازگشایی" }, "enableContextMenuItem": { "message": "نمایش گزینه‌های منوی زمینه" @@ -796,7 +796,7 @@ "message": "۱ گیگابایت فضای ذخیره سازی رمزگذاری شده برای پیوست های پرونده." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "گزینه های ورود اضافی دو مرحله ای مانند YubiKey و Duo." }, "ppremiumSignUpReports": { "message": "گزارش‌های بهداشت رمز عبور، سلامت حساب و نقض داده‌ها برای ایمن نگهداشتن گاوصندوق شما." @@ -1606,10 +1606,10 @@ "message": "بیومتریک مرورگر در این دستگاه پشتیبانی نمی‌شود." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "زیست‌سنجی ناتمام ماند" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "زیست‌سنجی نمی تواند انجام شود، استفاده از کلمه عبور اصلی یا خروج را در نظر بگیرید. اگر این مشکل ادامه یافت لطفا با پشتیبانی Bitwarden تماس بگیرید." }, "nativeMessaginPermissionErrorTitle": { "message": "مجوز ارائه نشده است" @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "برون ریزی گاو‌صندوق شخصی" }, - "exportingPersonalVaultDescription": { - "message": "فقط موارد گاو‌صندوق شخصی مرتبط با $EMAIL$ برون ریزی خواهد شد. موارد گاو‌صندوق سازمان شامل نخواهد شد.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "نسخه سرور" }, "selfHostedServer": { - "message": "self-hosted" + "message": "خود میزبان" }, "thirdParty": { "message": "شخص ثالث" @@ -2153,7 +2153,7 @@ "message": "یک اعلان به دستگاه شما ارسال شده است." }, "loginInitiated": { - "message": "Login initiated" + "message": "ورود به سیستم آغاز شد" }, "exposedMasterPassword": { "message": "کلمه عبور اصلی افشا شده" @@ -2234,34 +2234,34 @@ } }, "loggingInOn": { - "message": "Logging in on" + "message": "ورود به عنوان" }, "opensInANewWindow": { "message": "در پنجره جدید باز می‌شود" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "تایید دستگاه لازم است. یک روش تایید برگزینید:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "این دستگاه را به خاطر بسپار" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "بردارید اگر از دستگاه عمومی استفاده میکنید" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "تایید با دستگاه دیگرتان" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "درخواست تایید مدیر" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "تایید با کلمه عبور اصلی" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "شناسه سازمان SSO مورد نیاز است." }, "eu": { - "message": "EU", + "message": "اروپا", "description": "European Union" }, "usDomain": { @@ -2280,40 +2280,40 @@ "message": "نمایش" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "حساب کاربری با موفقیت ایجاد شد!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "تایید مدیر در خواست شد" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "درخواست شما به مدیرتان فرستاده شد." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "به محض تایید مطلع خواهید شد." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "در ورود مشکلی دارید؟" }, "loginApproved": { - "message": "Login approved" + "message": "ورود تایید شد" }, "userEmailMissing": { - "message": "User email missing" + "message": "رایانامه کاربر کم است" }, "deviceTrusted": { - "message": "Device trusted" + "message": "دستگاه مورد اعتماد است" }, "inputRequired": { - "message": "Input is required." + "message": "ورودی مورد نیاز است." }, "required": { - "message": "required" + "message": "الزامی" }, "search": { - "message": "Search" + "message": "جستجو" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "ورودی باید حداقل $COUNT$ نشانه داشته باشد.", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2322,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "اندازه ورودی نباید بیش از $COUNT$ نشانه باشد.", "placeholders": { "count": { "content": "$1", @@ -2331,7 +2331,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "نشانه های زیر مجاز نیستند: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2340,7 +2340,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "مقدار ورودی باید دست کم $MIN$ باشد.", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2349,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "مقدار ورودی نباید بیش از $MAX$ باشد.", "placeholders": { "max": { "content": "$1", @@ -2358,17 +2358,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "یک یا چند رایانامه نامعتبر است" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "ورودی نباید فقط فاصله باشد.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "ورودی یک نشانی رایانامه نیست." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "بخش (های) $COUNT$ در بالا نیازمند توجه شما است.", "placeholders": { "count": { "content": "$1", @@ -2377,22 +2377,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- انتخاب --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- برای گزینش چیزی بنویسید --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "در حال بازیابی گزینه‌ها..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "موردی پیدا نشد" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "پاک کردن همه" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ بیشتر", "placeholders": { "quantity": { "content": "$1", @@ -2401,10 +2401,10 @@ } }, "submenu": { - "message": "Submenu" + "message": "زیرفهرست" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "باز و بسته کردن", "description": "Toggling an expand/collapse state." } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 2abd01eefa6..5d9a88f8c66 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Henkilökohtaisen holvin vienti" }, - "exportingPersonalVaultDescription": { - "message": "Vain tunnukseen $EMAIL$ liitetyt henkilökohtaisen holvin kohteet viedään. Organisaation kohteet eivät sisälly tähän.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Palvelimen versio" }, "selfHostedServer": { - "message": "self-hosted" + "message": "itse ylläpidetty" }, "thirdParty": { "message": "Ulkopuolinen taho" diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 3f046898751..cbdd895d03b 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Nag-export ng indibidwal na vault" }, - "exportingPersonalVaultDescription": { - "message": "Lamang ang mga item ng indibidwal na vault na nauugnay sa $EMAIL$ ang maie-export. Hindi kasama ang mga item ng vault ng organisasyon.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index d9dcd906c10..19a9adc5de4 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Export du coffre personnel" }, - "exportingPersonalVaultDescription": { - "message": "Seuls les éléments individuels du coffre associés à $EMAIL$ seront exportés. Les éléments du coffre de l'organisation ne seront pas inclus.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Version du serveur" }, "selfHostedServer": { - "message": "self-hosted" + "message": "auto-hébergé" }, "thirdParty": { "message": "Tierce partie" diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index d199a2e8db9..c3cd77bc639 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "הכספת האישית מיוצאת" }, - "exportingPersonalVaultDescription": { - "message": "רק פריטי הכספת האישית שמשויכת אל $EMAIL$ ייוצאו. פריטי הכספת הארגוניים לא יהיו חלק מהייצוא.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index e1d89271f21..697f04154ce 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index ee296293572..1e81fd2d757 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -339,7 +339,7 @@ "message": "Ostalo" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "Za promjenu vremena isteka trezora, odredi način otključavanja." }, "rateExtension": { "message": "Ocijeni proširenje" @@ -634,10 +634,10 @@ "message": "Ažuriraj" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "Za dovršetak auto-ispune, otključaj svoj trezor." }, "notificationUnlock": { - "message": "Unlock" + "message": "Otključaj" }, "enableContextMenuItem": { "message": "Prikaži opcije kotekstualnog izbornika" @@ -796,7 +796,7 @@ "message": "1 GB šifriranog prostora za pohranu podataka." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Mogućnosti za prijavu u dva koraka kao što su YubiKey i Duo." }, "ppremiumSignUpReports": { "message": "Higijenu lozinki, zdravlje računa i izvještaje o krađi podatak radi zaštite svojeg trezora." @@ -1456,7 +1456,7 @@ "message": "Odjava će ukloniti pristup tvom trezoru i zahtijevati mrežnu potvrdu identiteta nakon isteka vremenske neaktivnosti. Sigurno želiš koristiti ovu postavku?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Potvrda akcije vremenske neaktivnosti" + "message": "Potvrda radnje nakon vremenske neaktivnosti" }, "autoFillAndSave": { "message": "Auto-ispuni i spremi" @@ -1468,16 +1468,16 @@ "message": "Auto-ispunjena stavka" }, "insecurePageWarning": { - "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + "message": "Upozorenje: Ovo je nezaštićena HTTP stranica i svi podaci koje preko nje pošalješ drugi mogu vidjeti i izmijeniti. Ova prijava je prvotno bila spremljena za sigurnu (HTTPS) stranicu." }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "Želiš li i dalje ispuniti ove podatke za prijavu?" }, "autofillIframeWarning": { - "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + "message": "Obrazac je na poslužitelju koji se nalazi na drugačijoj domeni od URI-a za koji su spremljeni tvoji podaci za pristup. Odobri za auto-ispunu ili odustani za prekid." }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "Kako se ovo upozorenje ubuduće ne bi prikazivalo, spremi ovaj URI ( $HOSTNAME$) u svoju stavku za prijavu.", "placeholders": { "hostname": { "content": "$1", @@ -1489,13 +1489,13 @@ "message": "Postavi glavnu lozinku" }, "currentMasterPass": { - "message": "Current master password" + "message": "Trenutna glavna lozinka" }, "newMasterPass": { - "message": "New master password" + "message": "Nova glavna lozinka" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "Potvrdi novu glavnu lozinku" }, "masterPasswordPolicyInEffect": { "message": "Jedno ili više pravila organizacije zahtijeva da tvoja glavna lozinka ispunjava sljedeće uvjete:" @@ -1606,10 +1606,10 @@ "message": "Biometrija preglednika nije podržana na ovom uređaju." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "Biometrija neuspješna" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "Biometrija se ne može dovršiti. Pokušaj glavnom lozinkom ili se odjavi i ponovno prijavi. Ako se ovo nastavi, obrati se Bitwarden podršci." }, "nativeMessaginPermissionErrorTitle": { "message": "Dopuštenje nije dano" @@ -1884,7 +1884,7 @@ "message": "Tvoju glavnu lozinku je nedavno promijenio administrator tvoje organizacije. Za pristup trezoru, potrebno je ažurirati glavnu lozinku, što će te odjaviti iz trenutne sesije, te ćeš se morati ponovno prijaviti. Aktivne sesije na drugim uređajima mogu ostati aktivne još sat vremena." }, "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": "Tvoja glavna lozinka ne zadovoljava pravila ove organizacije. Za pristup trezoru moraš odmah ažurirati svoju glavnu lozinku. Ako nastaviš, odjaviti ćeš se iz trenutne sesije te ćeš se morati ponovno prijaviti. Aktivne sesije na drugim uređajima mogu ostati aktivne do jedan sat." }, "resetPasswordPolicyAutoEnroll": { "message": "Automatsko učlanjenje" @@ -1905,7 +1905,7 @@ "message": "minuta" }, "vaultTimeoutPolicyInEffect": { - "message": "Pravilo tvoje organizacije utječe na istek trezora. Najveće dozvoljeno vrijeme isteka je $HOURS$:$MINUTES$ h.", + "message": "Pravilo tvoje organizacije podesilo je najveće dozvoljeno vrijeme isteka trezora na $HOURS$:$MINUTES$ h.", "placeholders": { "hours": { "content": "$1", @@ -1918,7 +1918,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "Pravilo tvoje organizacije utječe na istek trezora. Najveće dozvoljeno vrijeme isteka je $HOURS$:$MINUTES$ h. Tvoja radnja nakon isteka trezora je: $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -1935,7 +1935,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "Pravilo tvoje organizacije podesilo je radnju nakon isteka trezora na: $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Izvoz osobnog trezora" }, - "exportingPersonalVaultDescription": { - "message": "Izvest će se samo stavke osobnog trezora povezanog s $EMAIL$. Stavke organizacijskog trezora neće biti uključene.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Verzija poslužitelja" }, "selfHostedServer": { - "message": "self-hosted" + "message": "vlastiti poslužitelj" }, "thirdParty": { "message": "Third-party" @@ -2153,7 +2153,7 @@ "message": "Obavijest je poslana na tvoj uređaj." }, "loginInitiated": { - "message": "Login initiated" + "message": "Prijava pokrenuta" }, "exposedMasterPassword": { "message": "Ukradena glavna lozinka" @@ -2234,31 +2234,31 @@ } }, "loggingInOn": { - "message": "Logging in on" + "message": "Prijava na" }, "opensInANewWindow": { - "message": "Opens in a new window" + "message": "Otvara u novom prozoru" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Potrebno je odobriti uređaj. Odaberi metodu odobravanja:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Zapamti ovaj uređaj" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Odznači ako koristiš javni uređaj" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Odobri drugim uređajem" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Zatraži odobrenje administratora" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Odobri glavnom lozinkom" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Potreban je identifikator organizacije." }, "eu": { "message": "EU", @@ -2271,49 +2271,49 @@ "message": "bitwarden.eu" }, "accessDenied": { - "message": "Access denied. You do not have permission to view this page." + "message": "Pristup odbijen. Nemaš prava vidjeti ovu stranicu." }, "general": { - "message": "General" + "message": "Opće" }, "display": { - "message": "Display" + "message": "Prikaz" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Račun je uspješno stvoren!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Zatraženo odobrenje administratora" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "Tvoj zahtjev je poslan administratoru." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Dobiti ćeš obavijest kada bude odobreno." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Problem s prijavom?" }, "loginApproved": { - "message": "Login approved" + "message": "Prijava odobrena" }, "userEmailMissing": { - "message": "User email missing" + "message": "Nedostaje e-pošta korisnika" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Uređaj pouzdan" }, "inputRequired": { - "message": "Input is required." + "message": "Potreban je unos." }, "required": { - "message": "required" + "message": "obavezno" }, "search": { - "message": "Search" + "message": "Traži" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "Unos mora sadržavati najmanje $COUNT$ znakova.", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2322,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "Unos ne smije imati više od $COUNT$ znakova.", "placeholders": { "count": { "content": "$1", @@ -2331,7 +2331,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Ovi znakovi nisu dozvoljeni: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2340,7 +2340,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "Unos mora biti najmanje $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2349,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "Unos ne smije biti više od $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2358,17 +2358,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "Jedna ili više adresa e-pošte nije valjana" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "Unos ne smije biti prazan.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "Nije unesena adresa e-pošte." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ polje/a treba tvoju pažnju.", "placeholders": { "count": { "content": "$1", @@ -2377,22 +2377,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Odaberi --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Upiši za filtriranje --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Dohvaćanje opcija..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Nije pronađena niti jedna stavka" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Očisti sve" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ još $QUANTITY$", "placeholders": { "quantity": { "content": "$1", @@ -2401,10 +2401,10 @@ } }, "submenu": { - "message": "Submenu" + "message": "Podizbornik" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Sažmi/Proširi", "description": "Toggling an expand/collapse state." } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 62a14307f6e..b18a03ee769 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Személyes széf exportálása" }, - "exportingPersonalVaultDescription": { - "message": "Csak $EMAIL$ email címmel társított személyes széf elemek kerülnek exportálásra. Ebbe nem kerülnek be a szervezeti széf elemek.", + "exportingIndividualVaultDescription": { + "message": "$EMAIL$ email címhez társított egyedi széfek kerülnek csak exportálásra. A szervezeti széf elemei nem lesznek benne. Csak a széf információk kerülnek exportálásra és nem tartalmazzák a kapcsolódó mellékleteket.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Szerver verzió" }, "selfHostedServer": { - "message": "self-hosted" + "message": "saját üzemeltetésű" }, "thirdParty": { "message": "Harmadik fél" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 5b1d144591d..e51078ece28 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index ad335ccd56c..c68e7aea3d3 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Esportazione cassaforte personale" }, - "exportingPersonalVaultDescription": { - "message": "Solo gli elementi della cassaforte personale associati a $EMAIL$ saranno esportati. Gli elementi della cassaforte dell'organizzazione non saranno inclusi.", + "exportingIndividualVaultDescription": { + "message": "Solo gli elementi della cassaforte personale associati a $EMAIL$ saranno esportati. Gli elementi della cassaforte dell'organizzazione non saranno inclusi. Solo le informazioni sugli elementi della cassaforte saranno esportate e non includeranno gli allegati.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 629a1644510..f51e783f3d5 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "個人保管庫のエクスポート" }, - "exportingPersonalVaultDescription": { - "message": "$EMAIL$ に関連付けられた個人用保管庫アイテムのみがエクスポートされます。組織用保管庫アイテムは含まれません。", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "サーバーのバージョン" }, "selfHostedServer": { - "message": "self-hosted" + "message": "自己ホスト型" }, "thirdParty": { "message": "サードパーティー" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index f950febf1f3..ea38b1f9a47 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 3635ce719ba..7762aa74db5 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index c8ad9cff366..995c5a932dd 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "개인 보관함을 내보내는 중" }, - "exportingPersonalVaultDescription": { - "message": "오직 $EMAIL$와 연관된 개인 보관함의 항목만 내보내집니다. 조직 보관함의 항목은 포함되지 않습니다.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index b8f9c6cf12a..f66d8f3f642 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index dfe5405e037..7388fa129ef 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -92,7 +92,7 @@ "message": "Automātiskā aizpildīšana" }, "generatePasswordCopied": { - "message": "Veidot paroli (ievietota starpliktuvē)" + "message": "Izveidot paroli (tiks ievietota starpliktuvē)" }, "copyElementIdentifier": { "message": "Pavairot pielāgotā lauka nosaukumu" @@ -467,7 +467,7 @@ "message": "Nederīgs apstiprinājuma kods" }, "valueCopied": { - "message": "$VALUE$ ievietota starpliktuvē", + "message": "$VALUE$ ir starpliktuvē", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -901,7 +901,7 @@ "message": "Šim kontam ir iespējota divpakāpju pieteikšanās, bet šajā pārlūkā netiek atbalstīts neviens no uzstādītajiem divpakāpju pārbaudes nodrošinātājiem." }, "noTwoStepProviders2": { - "message": "Lūgums izmantot atbalstītu tīmekļa pārlūku (piemēram Chrome) un/vai pievienot papildus nodrošinātājus, kas tiek labāk atbalstīti dažādos pārlūkos (piemēram autentificētāja lietotni)." + "message": "Lūgums izmantot atbalstītu tīmekļa pārlūku (piemēram Chrome) un/vai pievienot papildu nodrošinātājus, kas tiek labāk atbalstīti dažādos pārlūkos (piemēram autentificētāja lietotni)." }, "twoStepOptions": { "message": "Divpakāpju pieteikšanās iespējas" @@ -923,21 +923,21 @@ "message": "YubiKey OTP drošības atslēga" }, "yubiKeyDesc": { - "message": "Izmanto YubiKey, lai piekļūtu savam kontam! Darbojas ar YubiKey 4, 4 Nano, 4C un NEO ierīcēm." + "message": "Ir izmantojams YubiKey, lai piekļūtu savam kontam. Darbojas ar YubiKey 4, 4 Nano, 4C un NEO ierīcēm." }, "duoDesc": { - "message": "Apstiprini ar Duo Security, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu!", + "message": "Ar Duo Security apliecināšanu var veikt ar Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Apstiprini ar Duo Security savā apvienībā, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu!", + "message": "Apliecināšana ar savas apvienības Duo Security, izmantojot Duo Mobile lietotni, īsziņu, tālruņa zvanu vai U2F drošības atslēgu.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Izmantot jebkuru WebAuthn atbalstošu drošības atslēgu, lai piekļūtu kontam." + "message": "Ir izmantojama jebkura WebAuthn atbalstošu drošības atslēgu, lai piekļūtu kontam." }, "emailTitle": { "message": "E-pasts" @@ -1540,7 +1540,7 @@ "message": "Jaunā galvenā parole neatbilst nosacījumu prasībām." }, "acceptPolicies": { - "message": "Atzīmējot šo rūtiņu, Tu piekrīti sekojošajam:" + "message": "Ar šīs rūtiņas atzīmēšanu tiek piekrists sekojošajam:" }, "acceptPoliciesRequired": { "message": "Nav apstiprināti izmantošanas noteikumi un privātuma nosacījumi." @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Izdod personīgo glabātavu" }, - "exportingPersonalVaultDescription": { - "message": "Tiks izdoti tikai personīgie glabātavas vienumi, kas ir saistīti ar $EMAIL$. Apvienības glabātavas vienumi netiks iekļauti.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Servera versija" }, "selfHostedServer": { - "message": "self-hosted" + "message": "pašizvietots" }, "thirdParty": { "message": "Trešās puses" diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index dab1da00605..8a377bb32a6 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 4401946ca5d..9c83fe4e03c 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 5a3cb1a6675..98715f38327 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Eksporterer personlig hvelv" }, - "exportingPersonalVaultDescription": { - "message": "Bare de personlige hvelv-elementene som er knyttet til $EMAIL$ vil bli eksportert. Organisasjonshvelvets elementer vil ikke bli inkludert.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 7149b18ff68..623b16daf7f 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Persoonlijke kluis exporteren" }, - "exportingPersonalVaultDescription": { - "message": "Exporteert alleen de persoonlijke kluis-items gerelateerd aan $EMAIL$. Geen kluis-items van de organisatie.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index c2ec8abe5d0..dcc01d2284d 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Eksportowanie osobistego sejfu" }, - "exportingPersonalVaultDescription": { - "message": "Tylko osobiste elementy sejfu powiązane z adresem $EMAIL$ zostaną wyeksportowane. Elementy sejfu należące do organizacji nie będą uwzględnione.", + "exportingIndividualVaultDescription": { + "message": "Z sejfu zostaną wyeksportowane tylko elementy powiązane z $EMAIL$. Elementy z sejfu organizacji nie będą uwzględnione. Tylko informacje o elemencie zostaną wyeksportowane i nie będą zawierać powiązanych załączników.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Wersja serwera" }, "selfHostedServer": { - "message": "self-hosted" + "message": "samodzielnie hostowany" }, "thirdParty": { "message": "Inny dostawca" diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 474174a36e9..f3a760a426f 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exportando o Cofre Pessoal" }, - "exportingPersonalVaultDescription": { - "message": "Apenas os itens pessoais do cofre associados com $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 28362dc31ad..5c80b8dfa12 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "A exportar o cofre pessoal" }, - "exportingPersonalVaultDescription": { - "message": "Apenas os itens do cofre pessoal associado a $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos.", + "exportingIndividualVaultDescription": { + "message": "Apenas os itens de cofre individuais associados a $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos. Apenas serão exportadas as informações do item do cofre e não serão incluídos os anexos associados.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Versão do servidor" }, "selfHostedServer": { - "message": "self-hosted" + "message": "auto-hospedado" }, "thirdParty": { "message": "De terceiros" diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 40c86ecf37d..1ee069177ee 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exportul seifului individual" }, - "exportingPersonalVaultDescription": { - "message": "Numai articolele de seif individuale asociate cu $EMAIL$ vor fi exportate. Articolele de seif ale organizației nu vor fi incluse.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index df0906a0ec1..f6d8d8b2d4f 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Экспорт личного хранилища" }, - "exportingPersonalVaultDescription": { - "message": "Будут экспортированы только личные элементы хранилища, связанные с $EMAIL$. Элементы хранилища организации включены не будут.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Версия сервера" }, "selfHostedServer": { - "message": "self-hosted" + "message": "собственный хостинг" }, "thirdParty": { "message": "Сторонний" diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 1236f44e6eb..d42711812c8 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 5778db28f79..267ec80df9e 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exportovanie osobného trezora" }, - "exportingPersonalVaultDescription": { - "message": "Exportované budú iba položy osobného trezora spojené s $EMAIL$. Položky trezora organizácie nebudú zahrnuté.", + "exportingIndividualVaultDescription": { + "message": "Exportované budú iba položky súvisiace s $EMAIL$. Položky z trezora organizácie nebudú zahrnuté v exporte. Export bude obsahovať iba informácie z položiek v trezore, súvisiace prílohy nebudú súčasťou exportu.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Verzia servera" }, "selfHostedServer": { - "message": "self-hosted" + "message": "vlastný hosting" }, "thirdParty": { "message": "Tretia strana" diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 06967ee2166..0e05815294d 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Izvoženi bodo samo posamezni elementi trezorja, ki so povezani z $EMAIL$. Elementi trezorjev organizacij ne bodo izvoženi.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index f823208d1dd..72d99fcecce 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Извоз личног сефа" }, - "exportingPersonalVaultDescription": { - "message": "Само предмети личног сефа повезани са $EMAIL$ биће извезени. Ставке организационог сефа неће бити укључене.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Верзија сервера" }, "selfHostedServer": { - "message": "self-hosted" + "message": "личан хостинг" }, "thirdParty": { "message": "Трећа страна" diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 2c90c1c8f86..fb44f17c835 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -634,10 +634,10 @@ "message": "Uppdatera" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "Lås upp ditt Bitwarden-valv för att slutföra begäran om automatisk ifyllnad." }, "notificationUnlock": { - "message": "Unlock" + "message": "Lås upp" }, "enableContextMenuItem": { "message": "Visa alternativ för snabbmenyn" @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporterar individuellt valv" }, - "exportingPersonalVaultDescription": { - "message": "Endast de personliga valvobjekt som är associerade med $EMAIL$ kommer att exporteras. Organisationens valvobjekt kommer inte att inkluderas.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 43c3cc0b68e..6e95df17b01 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 97ce8ca58cc..d09c59940d1 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index e32dcb4ac81..d9cc7ae7485 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Kişisel kasa dışa aktarılıyor" }, - "exportingPersonalVaultDescription": { - "message": "Yalnızca $EMAIL$ ile ilişkili kişisel kasadaki kayıtlar dışa aktarılacaktır. Kuruluş kasasındaki kayıtlar dahil edilmeyecektir.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "Sunucu sürümü" }, "selfHostedServer": { - "message": "self-hosted" + "message": "şirket içinde barındırılan" }, "thirdParty": { "message": "Üçüncü taraf" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index c695aaadf2e..fea015d9932 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Експортування особистого сховища" }, - "exportingPersonalVaultDescription": { - "message": "Будуть експортовані лише записи особистого сховища, пов'язані з $EMAIL$. Записи сховища організації не буде включено.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 95716b3fc36..62f63995c7b 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "Exporting individual vault" }, - "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index eded4b220c7..5325ef6a4f1 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -41,7 +41,7 @@ "message": "主密码是您访问密码库的唯一密码。它非常重要,请您不要忘记。一旦忘记,无任何办法恢复此密码。" }, "masterPassHintDesc": { - "message": "主密码提示可以在你忘记密码时帮你回忆起来。" + "message": "主密码提示可以在您忘记密码时帮您回忆起来。" }, "reTypeMasterPass": { "message": "再次输入主密码" @@ -480,13 +480,13 @@ "message": "无法在此页面上自动填充所选项目。请改为手工复制并粘贴。" }, "loggedOut": { - "message": "已退出账户" + "message": "已注销" }, "loginExpired": { "message": "您的登录会话已过期。" }, "logOutConfirmation": { - "message": "您确定要退出账户吗?" + "message": "您确定要注销吗?" }, "yes": { "message": "是" @@ -1391,7 +1391,7 @@ "message": "使用 PIN 码解锁" }, "setYourPinCode": { - "message": "设置您用来解锁 Bitwarden 的 PIN 码。您的 PIN 设置将在您退出账户时被重置。" + "message": "设定您用来解锁 Bitwarden 的 PIN 码。您的 PIN 设置将在您完全注销此应用程序时被重置。" }, "pinRequired": { "message": "需要 PIN 码。" @@ -1453,7 +1453,7 @@ "message": "项目已恢复" }, "vaultTimeoutLogOutConfirmation": { - "message": "超时后退出账户将解除对密码库的所有访问权限,并需要进行在线身份验证。确定使用此设置吗?" + "message": "超时后注销账户将解除对密码库的所有访问权限,并需要进行在线身份验证。确定使用此设置吗?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "超时动作确认" @@ -1624,7 +1624,7 @@ "message": "此操作不能在侧边栏中完成,请在弹出窗口或弹出对话框中重试。" }, "personalOwnershipSubmitError": { - "message": "由于某个企业策略,您被限制为保存项目到您的个人密码库。将所有权选项更改为组织,然后从可用的集合中选择。" + "message": "由于某个企业策略,您不能将项目保存到您的个人密码库。将所有权选项更改为组织,并从可用的集合中选择。" }, "personalOwnershipPolicyInEffect": { "message": "一个组织策略正影响您的所有权选项。" @@ -1796,14 +1796,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { - "message": "新建密码" + "message": "新密码" }, "sendDisabled": { "message": "Send 已禁用", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "由于企业策略,您只能删除现有的 Send。", + "message": "由于某个企业策略,您只能删除现有的 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { @@ -1860,7 +1860,7 @@ "message": "一个或多个组织策略正在影响您的 Send 选项。" }, "passwordPrompt": { - "message": "重新询问主密码" + "message": "主密码重新提示" }, "passwordConfirmation": { "message": "确认主密码" @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "导出个人密码库" }, - "exportingPersonalVaultDescription": { - "message": "仅会导出与 $EMAIL$ 关联的个人密码库项目。组织密码库的项目不会导出。", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2093,7 +2093,7 @@ "message": "服务器版本" }, "selfHostedServer": { - "message": "self-hosted" + "message": "自托管" }, "thirdParty": { "message": "第三方" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 96bdfaaa4df..c436fd63a29 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -637,7 +637,7 @@ "message": "Unlock your Bitwarden vault to complete the auto-fill request." }, "notificationUnlock": { - "message": "Unlock" + "message": "解鎖" }, "enableContextMenuItem": { "message": "顯示內容選單選項" @@ -985,7 +985,7 @@ "message": "網頁載入時如果偵測到登入表單,則執行自動填入。" }, "experimentalFeature": { - "message": "被入侵或不可信任的網站可以利用自動填入功能在網頁載入時竊取資訊。" + "message": "被入侵或不可信任的網站可以利用網頁載入時的自動填入功能。" }, "learnMoreAboutAutofill": { "message": "進一步瞭解「自動填入」功能" @@ -1264,10 +1264,10 @@ "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "檢查密碼是否已外洩。" + "message": "檢查密碼是否已暴露。" }, "passwordExposed": { - "message": "此密碼已外洩了 $VALUE$ 次,應立即變更密碼。", + "message": "此密碼在資料外洩事件中被暴露了 $VALUE$ 次,應立即變更它。", "placeholders": { "value": { "content": "$1", @@ -1495,7 +1495,7 @@ "message": "新的主密碼" }, "confirmNewMasterPass": { - "message": "確認新主密碼" + "message": "確認新的主密碼" }, "masterPasswordPolicyInEffect": { "message": "一個或多個組織原則要求您的主密碼須符合下列條件:" @@ -1991,8 +1991,8 @@ "exportingPersonalVaultTitle": { "message": "正匯出個人密碼庫" }, - "exportingPersonalVaultDescription": { - "message": "只會匯出與 $EMAIL$ 關聯的個人密碼庫項目。組織密碼庫的項目不包含在內。", + "exportingIndividualVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", "placeholders": { "email": { "content": "$1", @@ -2153,13 +2153,13 @@ "message": "已傳送通知至您的裝置。" }, "loginInitiated": { - "message": "Login initiated" + "message": "登入已發起" }, "exposedMasterPassword": { "message": "已暴露的主密碼" }, "exposedMasterPasswordDesc": { - "message": "在其他資料庫中找到您的密碼。我們建議您使用一個獨特的密碼來保護您的帳號,您確定要用這個密碼嗎?" + "message": "在資料外洩事件中找到了密碼。我們建議您使用一個獨特的密碼來保護您的帳戶,您確定要使用已暴露的密碼嗎?" }, "weakAndExposedMasterPassword": { "message": "強度不足且已暴露的主密碼" @@ -2171,7 +2171,7 @@ "message": "檢查外洩密碼資料庫中是否有此密碼" }, "important": { - "message": "重要事項:" + "message": "重要:" }, "masterPasswordHint": { "message": "如果您忘記主密碼,沒有復原的方法!" @@ -2234,7 +2234,7 @@ } }, "loggingInOn": { - "message": "Logging in on" + "message": "正登入到" }, "opensInANewWindow": { "message": "在新視窗開啟" @@ -2280,7 +2280,7 @@ "message": "顯示" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "已成功建立帳戶!" }, "adminApprovalRequested": { "message": "Admin approval requested" @@ -2310,10 +2310,10 @@ "message": "required" }, "search": { - "message": "Search" + "message": "搜尋" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "必須輸入至少 $COUNT$ 個字元。", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2322,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "輸入的內容長度不得超過 $COUNT$ 字元。", "placeholders": { "count": { "content": "$1", From fc4379f3928adebaee2c5e9cd2ca058d87fea009 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 14 Sep 2023 16:29:15 +0200 Subject: [PATCH 132/135] [CL-128] Support placeholders in i18n mock (#6254) --- .../src/form-field/error-summary.stories.ts | 6 +++--- libs/components/src/utils/i18n-mock.service.ts | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libs/components/src/form-field/error-summary.stories.ts b/libs/components/src/form-field/error-summary.stories.ts index 95061df81b6..4e1031abaf6 100644 --- a/libs/components/src/form-field/error-summary.stories.ts +++ b/libs/components/src/form-field/error-summary.stories.ts @@ -24,7 +24,7 @@ export default { required: "required", inputRequired: "Input is required.", inputEmail: "Input is not an email-address.", - fieldsNeedAttention: "$COUNT$ field(s) above need your attention.", + fieldsNeedAttention: "__$1__ field(s) above need your attention.", }); }, }, @@ -63,12 +63,12 @@ export const Default: StoryObj = { Name - + Email - + diff --git a/libs/components/src/utils/i18n-mock.service.ts b/libs/components/src/utils/i18n-mock.service.ts index 25e9a6db678..1dcc84b4a99 100644 --- a/libs/components/src/utils/i18n-mock.service.ts +++ b/libs/components/src/utils/i18n-mock.service.ts @@ -12,8 +12,20 @@ export class I18nMockService implements I18nService { constructor(private lookupTable: Record string)>) {} t(id: string, p1?: string, p2?: string, p3?: string) { - const value = this.lookupTable[id]; + let value = this.lookupTable[id]; if (typeof value == "string") { + if (value !== "") { + if (p1 != null) { + value = value.split("__$1__").join(p1.toString()); + } + if (p2 != null) { + value = value.split("__$2__").join(p2.toString()); + } + if (p3 != null) { + value = value.split("__$3__").join(p3.toString()); + } + } + return value; } return value(p1, p2, p3); From 3507d318e5d04526bc3afae4cb698663ad3a2b8d Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 14 Sep 2023 16:29:46 +0200 Subject: [PATCH 133/135] [PM-3625] Remove ClientType.DirectoryConnector (#6099) --- apps/desktop/src/app/services/services.module.ts | 13 +------------ .../services/electron-platform-utils.service.ts | 10 ++-------- libs/angular/src/services/injection-tokens.ts | 1 - libs/common/src/enums/client-type.enum.ts | 4 ++-- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 87fb1eecfcb..c6fd0049901 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -4,7 +4,6 @@ import { SECURE_STORAGE, STATE_FACTORY, STATE_SERVICE_USE_CACHE, - CLIENT_TYPE, LOCALES_DIRECTORY, SYSTEM_LANGUAGE, MEMORY_STORAGE, @@ -15,7 +14,6 @@ import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/adm import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service"; import { LoginService } from "@bitwarden/common/auth/services/login.service"; -import { ClientType } from "@bitwarden/common/enums"; import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -82,10 +80,6 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); provide: STATE_FACTORY, useValue: new StateFactory(GlobalState, Account), }, - { - provide: CLIENT_TYPE, - useValue: ClientType.Desktop, - }, { provide: RELOAD_CALLBACK, useValue: null, @@ -94,12 +88,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); { provide: PlatformUtilsServiceAbstraction, useClass: ElectronPlatformUtilsService, - deps: [ - I18nServiceAbstraction, - MessagingServiceAbstraction, - CLIENT_TYPE, - StateServiceAbstraction, - ], + deps: [I18nServiceAbstraction, MessagingServiceAbstraction], }, { provide: I18nServiceAbstraction, diff --git a/apps/desktop/src/platform/services/electron-platform-utils.service.ts b/apps/desktop/src/platform/services/electron-platform-utils.service.ts index 485490c0a87..6f4f55b4f4b 100644 --- a/apps/desktop/src/platform/services/electron-platform-utils.service.ts +++ b/apps/desktop/src/platform/services/electron-platform-utils.service.ts @@ -4,7 +4,6 @@ import { ClientType, DeviceType } from "@bitwarden/common/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricMessage, BiometricStorageAction } from "../../types/biometric-message"; import { isDev, isMacAppStore } from "../../utils"; @@ -12,12 +11,7 @@ import { isDev, isMacAppStore } from "../../utils"; export class ElectronPlatformUtilsService implements PlatformUtilsService { private deviceCache: DeviceType = null; - constructor( - protected i18nService: I18nService, - private messagingService: MessagingService, - private clientType: ClientType.Desktop | ClientType.DirectoryConnector, - private stateService: StateService - ) {} + constructor(protected i18nService: I18nService, private messagingService: MessagingService) {} getDevice(): DeviceType { if (!this.deviceCache) { @@ -44,7 +38,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { } getClientType() { - return this.clientType; + return ClientType.Desktop; } isFirefox(): boolean { diff --git a/libs/angular/src/services/injection-tokens.ts b/libs/angular/src/services/injection-tokens.ts index b87b239e162..af3350193ec 100644 --- a/libs/angular/src/services/injection-tokens.ts +++ b/libs/angular/src/services/injection-tokens.ts @@ -17,7 +17,6 @@ export const LOGOUT_CALLBACK = new InjectionToken< export const LOCKED_CALLBACK = new InjectionToken<(userId?: string) => Promise>( "LOCKED_CALLBACK" ); -export const CLIENT_TYPE = new InjectionToken("CLIENT_TYPE"); export const LOCALES_DIRECTORY = new InjectionToken("LOCALES_DIRECTORY"); export const SYSTEM_LANGUAGE = new InjectionToken("SYSTEM_LANGUAGE"); export const LOG_MAC_FAILURES = new InjectionToken("LOG_MAC_FAILURES"); diff --git a/libs/common/src/enums/client-type.enum.ts b/libs/common/src/enums/client-type.enum.ts index 246769ebf21..54653f74462 100644 --- a/libs/common/src/enums/client-type.enum.ts +++ b/libs/common/src/enums/client-type.enum.ts @@ -2,7 +2,7 @@ export enum ClientType { Web = "web", Browser = "browser", Desktop = "desktop", - Mobile = "mobile", + // Mobile = "mobile", Cli = "cli", - DirectoryConnector = "connector", + // DirectoryConnector = "connector", } From f0b56b0b56769958b89e29bf0d37d264b40a49aa Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 14 Sep 2023 18:31:48 +0200 Subject: [PATCH 134/135] [CL-60] Set font-size on html (#6278) --- .github/workflows/chromatic.yml | 2 +- libs/components/src/styles.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index eda9f8d0f7d..e47a2f40f53 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -44,4 +44,4 @@ jobs: storybookBuildDir: ./storybook-static exitOnceUploaded: true onlyChanged: true - externals: "[\"libs/components/**/*.scss\", \"libs/components/tailwind.config*.js\"]" + externals: "[\"libs/components/**/*.scss\", \"libs/components/**/*.css\", \"libs/components/tailwind.config*.js\"]" diff --git a/libs/components/src/styles.css b/libs/components/src/styles.css index 6714e5ddde6..ba200ff3f32 100644 --- a/libs/components/src/styles.css +++ b/libs/components/src/styles.css @@ -6,6 +6,6 @@ @tailwind components; @tailwind utilities; -body { +html { font-size: 14px; } From 8dc11a6f120cf91b0a2b129514f52528b9deaa43 Mon Sep 17 00:00:00 2001 From: Matt Bishop Date: Thu, 14 Sep 2023 14:29:04 -0400 Subject: [PATCH 135/135] Upgrade to latest electron v24 (#6295) --- apps/desktop/electron-builder.json | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 8e6d300218a..f1768f9b03b 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -19,7 +19,7 @@ "**/node_modules/@bitwarden/desktop-native/index.js", "**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node" ], - "electronVersion": "24.1.1", + "electronVersion": "24.8.3", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", diff --git a/package-lock.json b/package-lock.json index 9b07e036e22..de0f8a7ee59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -124,7 +124,7 @@ "cross-env": "7.0.3", "css-loader": "6.8.1", "del": "6.1.1", - "electron": "24.1.1", + "electron": "24.8.3", "electron-builder": "^23.6.0", "electron-log": "4.4.8", "electron-reload": "2.0.0-alpha.1", @@ -20209,9 +20209,9 @@ } }, "node_modules/electron": { - "version": "24.1.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-24.1.1.tgz", - "integrity": "sha512-ymjUMe6Pvh9ytpM4lOvr+Qxd6NG5AELRtR6tw54bK3FXfKtTTKKAtZw/NbwHwkRAlWu8FNAGOuvCoap6/bm9LQ==", + "version": "24.8.3", + "resolved": "https://registry.npmjs.org/electron/-/electron-24.8.3.tgz", + "integrity": "sha512-6YTsEOIMIQNnOz0Fj5gAWtwCuDd66iYKkuRJGyc80jKKYI49jBeH+R7ZZry9uiVu4T4bZgBN2FAN24XfCioQZA==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/package.json b/package.json index 6f25336db04..c7c0a64360b 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "cross-env": "7.0.3", "css-loader": "6.8.1", "del": "6.1.1", - "electron": "24.1.1", + "electron": "24.8.3", "electron-builder": "^23.6.0", "electron-log": "4.4.8", "electron-reload": "2.0.0-alpha.1",