diff --git a/.eslintignore b/.eslintignore index b6c475d332..c9a25670a9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,7 +8,6 @@ storybook-static **/webpack.*.js **/jest.config.js -**/gulpfile.js apps/browser/config/config.js apps/browser/src/auth/scripts/duo.js diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c050ee1f6c..cf656967da 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -95,12 +95,14 @@ libs/common/src/autofill @bitwarden/team-autofill-dev apps/desktop/macos/autofill-extension @bitwarden/team-autofill-dev # DuckDuckGo integration apps/desktop/native-messaging-test-runner @bitwarden/team-autofill-dev -apps/desktop/src/services/native-message-handler.service.ts @bitwarden/team-autofill-dev +apps/desktop/src/services/duckduckgo-message-handler.service.ts @bitwarden/team-autofill-dev + ## Component Library ## .storybook @bitwarden/team-design-system libs/components @bitwarden/team-design-system apps/browser/src/platform/popup/layout @bitwarden/team-design-system +apps/browser/src/popup/app-routing.animations.ts @bitwarden/team-design-system apps/web/src/app/layouts @bitwarden/team-design-system ## Desktop native module ## @@ -116,6 +118,7 @@ libs/key-management @bitwarden/team-key-management-dev apps/desktop/destkop_native/core/src/biometric/ @bitwarden/team-key-management-dev apps/desktop/src/services/native-messaging.service.ts @bitwarden/team-key-management-dev apps/browser/src/background/nativeMessaging.background.ts @bitwarden/team-key-management-dev +apps/desktop/src/services/biometric-message-handler.service.ts @bitwarden/team-key-management-dev ## Locales ## apps/browser/src/_locales/en/messages.json diff --git a/.github/renovate.json b/.github/renovate.json index 0172403f0f..9295ea5d61 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -39,6 +39,10 @@ "groupName": "macOS/iOS bindings", "matchPackageNames": ["core-foundation", "security-framework", "security-framework-sys"] }, + { + "groupName": "zbus", + "matchPackageNames": ["zbus", "zbus_polkit"] + }, { "matchPackageNames": [ "base64-loader", @@ -75,11 +79,6 @@ "concurrently", "cross-env", "del", - "gulp", - "gulp-if", - "gulp-json-editor", - "gulp-replace", - "gulp-zip", "nord", "patch-package", "prettier", diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 350835d0ec..6e5e11c336 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -120,7 +120,6 @@ jobs: whoami node --version npm --version - gulp --version docker --version echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" diff --git a/apps/browser/package.json b/apps/browser/package.json index 53500a83d2..647847db45 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2024.11.2", + "version": "2024.12.0", "scripts": { "build": "npm run build:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 45ff01b0b7..f98705ce1b 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -3,39 +3,39 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden Password Manager", + "message": "مدير كلمات المرور Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information", + "message": "في المنزل، في العمل، أو في أثناء التنقل، يقوم Bitwarden بتأمين جميع كلمات المرور والمعلومات الحساسة بسهولة", "description": "Extension description, MUST be less than 112 characters (Safari restriction)" }, "loginOrCreateNewAccount": { - "message": "قم بالتسجيل أو إنشاء حساب جديد للوصول إلى خزنتك الآمنة." + "message": "قم بالتسجيل أو إنشاء حساب جديد للوصول إلى خزانتك الآمنة." }, "inviteAccepted": { - "message": "Invitation accepted" + "message": "تم قَبُول الدعوة" }, "createAccount": { "message": "إنشاء حساب" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "هل أنت جديد على Bitwarden؟" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "تسجيل الدخول باستخدام مفتاح المرور" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "استخدام تسجيل الدخول الأحادي" }, "welcomeBack": { - "message": "Welcome back" + "message": "مرحبًا بعودتك" }, "setAStrongPassword": { - "message": "Set a strong password" + "message": "أنشئ كلمة مرور قوية" }, "finishCreatingYourAccountBySettingAPassword": { - "message": "Finish creating your account by setting a password" + "message": "أكمل إنشاء حسابك عن طريق تعيين كلمة مرور" }, "enterpriseSingleSignOn": { "message": "تسجيل الدخول الأُحادي للمؤسسات – SSO" @@ -56,13 +56,13 @@ "message": "كلمة المرور الرئيسية" }, "masterPassDesc": { - "message": "كلمة المرور الرئيسية هي كلمة المرور التي تستخدمها للوصول إلى خزنتك. من المهم جدا ألا تنسى كلمة المرور الرئيسية. لا توجد طريقة لاسترداد كلمة المرور في حال نسيتها." + "message": "كلمة المرور الرئيسية هي كلمة المرور التي تستخدمها للوصول إلى خزانتك. من المهم جدا ألا تنسى كلمة المرور الرئيسية. لا توجد طريقة لاسترداد كلمة المرور في حال نسيتها." }, "masterPassHintDesc": { "message": "يمكن أن يساعدك تلميح كلمة المرور الرئيسية في تذكر كلمة المرور الخاصة بك في حال نسيتها." }, "masterPassHintText": { - "message": "If you forget your password, the password hint can be sent to your email. $CURRENT$/$MAXIMUM$ character maximum.", + "message": "إذا نسيت كلمة المرور الخاصة بك، يمكن إرسال تلميح كلمة المرور إلى بريدك الإلكتروني. الحد الأقصى لعدد الأحرف هو $CURRENT$/$MAXIMUM$.", "placeholders": { "current": { "content": "$1", @@ -81,7 +81,7 @@ "message": "تلميح كلمة المرور الرئيسية (إختياري)" }, "joinOrganization": { - "message": "Join organization" + "message": "انضم إلى المنظمة" }, "joinOrganizationName": { "message": "Join $ORGANIZATIONNAME$", @@ -99,13 +99,13 @@ "message": "علامة تبويب" }, "vault": { - "message": "الخزنة" + "message": "الخزانة" }, "myVault": { - "message": "خزنتي" + "message": "خزانتي" }, "allVaults": { - "message": "جميع الخزنات" + "message": "جميع الخزانات" }, "tools": { "message": "الأدوات" @@ -217,10 +217,10 @@ "message": "إضافة هوية" }, "unlockVaultMenu": { - "message": "افتح خزنتك" + "message": "افتح خزانتك" }, "loginToVaultMenu": { - "message": "تسجيل الدخول إلى خزنتك" + "message": "تسجيل الدخول إلى خزانتك" }, "autoFillInfo": { "message": "لا توجد تسجيلات دخول متاحة للملء التلقائي في علامة تبويب المتصفح الحالية." @@ -408,7 +408,7 @@ "message": "المزامنة" }, "syncVaultNow": { - "message": "مزامنة الخزنة الآن" + "message": "مزامنة الخزانة الآن" }, "lastSync": { "message": "آخر مزامنة:" @@ -534,7 +534,7 @@ "description": "Indicates that a policy limits the credential generator screen." }, "searchVault": { - "message": "البحث في الخزنة" + "message": "البحث في الخزانة" }, "edit": { "message": "تعديل" @@ -651,7 +651,7 @@ "message": "قم بتأكيد هويتك" }, "yourVaultIsLocked": { - "message": "خزنتك مقفلة. قم بتأكيد هويتك للمتابعة." + "message": "خزانتك مقفلة. قم بتأكيد هويتك للمتابعة." }, "yourVaultIsLockedV2": { "message": "Your vault is locked" @@ -682,7 +682,7 @@ "message": "كلمة المرور الرئيسية غير صالحة" }, "vaultTimeout": { - "message": "نفذ وقت الخزنة" + "message": "نفذ وقت الخزانة" }, "vaultTimeout1": { "message": "Timeout" @@ -888,7 +888,7 @@ "message": "أُضيف المجلد" }, "twoStepLoginConfirmation": { - "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزنة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" + "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزانة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, "twoStepLoginConfirmationContent": { "message": "Make your account more secure by setting up two-step login in the Bitwarden web app." @@ -984,7 +984,7 @@ "message": "Save to vault options" }, "addLoginNotificationDesc": { - "message": "اطلب إضافة عنصر إذا لم يُعثر عليه في خزنتك." + "message": "اطلب إضافة عنصر إذا لم يُعثر عليه في خزانتك." }, "addLoginNotificationDescAlt": { "message": "اطلب إضافة عنصر إذا لم يتم العثور على عنصر في المخزن الخاص بك. ينطبق على جميع حسابات تسجيل الدخول." @@ -1092,7 +1092,7 @@ "message": "Export from" }, "exportVault": { - "message": "تصدير الخزنة" + "message": "تصدير الخزانة" }, "fileFormat": { "message": "صيغة الملف" @@ -1125,11 +1125,15 @@ "message": "تحذير", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { - "message": "تأكيد تصدير الخزنة" + "message": "تأكيد تصدير الخزانة" }, "exportWarningDesc": { - "message": "يحتوي هذا التصدير على بيانات خزنتك بتنسيق غير مشفر. لا يجب عليك تخزين أو إرسال الملف الذي تم تصديره عبر قنوات غير آمنة (مثل البريد الإلكتروني). احذفه مباشرة بعد انتهائك من استخدامه." + "message": "يحتوي هذا التصدير على بيانات خزانتك بتنسيق غير مشفر. لا يجب عليك تخزين أو إرسال الملف الذي تم تصديره عبر قنوات غير آمنة (مثل البريد الإلكتروني). احذفه مباشرة بعد انتهائك من استخدامه." }, "encExportKeyWarningDesc": { "message": "يقوم هذا التصدير بتشفير بياناتك باستخدام مفتاح تشفير حسابك. إذا قمت بتدوير مفتاح تشفير حسابك يجب عليك التصدير مرة أخرى لأنك لن تتمكن من فك تشفير ملف التصدير هذا." @@ -1138,7 +1142,7 @@ "message": "مفاتيح تشفير الحساب فريدة من نوعها لكل حساب مستخدم Bitwarden، لذلك لا يمكنك استيراد تصدير مشفر إلى حساب آخر." }, "exportMasterPassword": { - "message": "أدخل كلمة المرور الرئيسية لتصدير بيانات خزنتك." + "message": "أدخل كلمة المرور الرئيسية لتصدير بيانات خزانتك." }, "shared": { "message": "مشترك" @@ -1247,10 +1251,10 @@ "message": "خيارات تسجيل الدخول بخطوتين المملوكة لجهات اخرى مثل YubiKey و Duo." }, "ppremiumSignUpReports": { - "message": "نظافة كلمة المرور، صحة الحساب، وتقارير تسريبات البيانات للحفاظ على سلامة خزنتك." + "message": "نظافة كلمة المرور، صحة الحساب، وتقارير تسريبات البيانات للحفاظ على سلامة خزانتك." }, "ppremiumSignUpTotp": { - "message": "مورد رمز التحقق (2FA) لتسجيل الدخول في خزنتك." + "message": "مورد رمز التحقق (2FA) لتسجيل الدخول في خزانتك." }, "ppremiumSignUpSupport": { "message": "أولوية دعم العملاء." @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "إذا تم اكتشاف نموذج تسجيل الدخول، يتم التعبئة التلقائية عند تحميل صفحة الويب." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "مواقع المساومة أو غير الموثوق بها يمكن أن تستغل الملء التلقائي في تحميل الصفحة." }, @@ -2108,7 +2099,7 @@ "message": "Already have an account?" }, "vaultTimeoutLogOutConfirmation": { - "message": "سيؤدي تسجيل الخروج إلى إزالة جميع إمكانية الوصول إلى خزنتك ويتطلب المصادقة عبر الإنترنت بعد انتهاء المهلة. هل أنت متأكد من أنك تريد استخدام هذا الإعداد؟" + "message": "سيؤدي تسجيل الخروج إلى إزالة جميع إمكانية الوصول إلى خزانتك ويتطلب المصادقة عبر الإنترنت بعد انتهاء المهلة. هل أنت متأكد من أنك تريد استخدام هذا الإعداد؟" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "تأكيد إجراء المهلة" @@ -2686,7 +2677,7 @@ "message": "Email verified" }, "emailVerificationRequiredDesc": { - "message": "يجب عليك تأكيد بريدك الإلكتروني لاستخدام هذه الميزة. يمكنك تأكيد بريدك الإلكتروني في خزنة الويب." + "message": "يجب عليك تأكيد بريدك الإلكتروني لاستخدام هذه الميزة. يمكنك تأكيد بريدك الإلكتروني في خزانة الويب." }, "updatedMasterPassword": { "message": "Updated master password" @@ -2695,7 +2686,7 @@ "message": "تحديث كلمة المرور الرئيسية" }, "updateMasterPasswordWarning": { - "message": "تم تغيير كلمة المرور الرئيسية الخاصة بك مؤخرًا من قبل مسؤول في مؤسستك. من أجل الوصول إلى الخزنة، يجب عليك تحديثها الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. قد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة." + "message": "تم تغيير كلمة المرور الرئيسية الخاصة بك مؤخرًا من قبل مسؤول في مؤسستك. من أجل الوصول إلى الخزانة، يجب عليك تحديثها الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. قد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة." }, "updateWeakMasterPasswordWarning": { "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." @@ -2747,7 +2738,7 @@ "message": "Enterprise policy requirements have been applied to your timeout options" }, "vaultTimeoutPolicyInEffect": { - "message": "سياسات مؤسستك تؤثر على مهلة الخزنة الخاص بك. الحد الأقصى المسموح به لمهلة الخزنة هو $HOURS$ ساعة/ساعات و $MINUTES$ دقيقة/دقائق", + "message": "سياسات مؤسستك تؤثر على مهلة الخزانة الخاص بك. الحد الأقصى المسموح به لمهلة الخزانة هو $HOURS$ ساعة/ساعات و $MINUTES$ دقيقة/دقائق.", "placeholders": { "hours": { "content": "$1", @@ -2812,10 +2803,10 @@ } }, "vaultTimeoutTooLarge": { - "message": "مهلة خزنتك تتجاوز القيود التي تضعها مؤسستك." + "message": "مهلة خزانتك تتجاوز القيود التي تضعها مؤسستك." }, "vaultExportDisabled": { - "message": "تصدير الخزنة مُعطّل" + "message": "تصدير الخزانة مُعطّل" }, "personalVaultExportPolicyInEffect": { "message": "واحدة أو أكثر من سياسات المؤسسة تمنعك من تصدير خزانتك الشخصية." @@ -2857,7 +2848,7 @@ "message": "انتهت مدة جلستك. يرجى العودة ومحاولة تسجيل الدخول مرة أخرى." }, "exportingPersonalVaultTitle": { - "message": "جاري تصدير الخزنة الشخصية" + "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.", @@ -3168,7 +3159,7 @@ "message": "عبارة بصمة الإصبع" }, "fingerprintMatchInfo": { - "message": "الرجاء التأكد من أن الخزنة الخاصة بك غير مقفلة وأن عبارة بصمة الإصبع تتطابق على الجهاز الآخر." + "message": "الرجاء التأكد من أن الخزانة الخاصة بك غير مقفلة وأن عبارة بصمة الإصبع تتطابق على الجهاز الآخر." }, "resendNotification": { "message": "إعادة إرسال الإشعار" @@ -3794,7 +3785,7 @@ } }, "confirmVaultImport": { - "message": "تأكيد تصدير الخزنة" + "message": "تأكيد تصدير الخزانة" }, "confirmVaultImportDesc": { "message": "هذا الملف محمي بكلمة مرور. الرجاء إدخال كلمة مرور الملف لاستيراد البيانات." @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index a21a962693..63e0c367e8 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -1125,6 +1125,10 @@ "message": "XƏBƏRDARLIQ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Xəbərdarlıq", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Seyfi xaricə köçürməyi təsdiqlə" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Giriş formu aşkarlananda, səhifə yüklənən zaman formu avto-doldurma icra edilsin." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Xəbərdarlıq:$CLOSETAG$ Təhlükəsizliyi pozulmuş və ya güvənilməyən veb saytlar, səhifə yüklənəndə avto-doldurmanı istifadə edə bilər.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Təhlükəli və ya güvənilməyən veb saytlar, səhifə yüklənərkən avto-doldurmanı istifadə edə bilər." }, @@ -3288,16 +3279,16 @@ "message": "Yeni bir pəncərədə açılır" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Gələcək girişləri problemsiz etmək üçün bu cihazı xatırla" }, "deviceApprovalRequired": { "message": "Cihaz təsdiqi tələb olunur. Aşağıdan bir təsdiq variantı seçin:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Cihaz təsdiqi tələb olunur" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Aşağıdan bir təsdiq seçimi edin" }, "rememberThisDevice": { "message": "Bu cihazı xatırla" @@ -3373,7 +3364,7 @@ "message": "İstifadəçi e-poçtu əskikdir" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktiv istifadəçi e-poçtu tapılmadı. Hesabınızdan çıxış edilir." }, "deviceTrusted": { "message": "Cihaz güvənlidir" @@ -3812,7 +3803,7 @@ "message": "Müraciət edilir" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Giriş edildi!" }, "passkeyNotCopied": { "message": "Keçid açarı kopyalanmır" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Uzantı eni" + }, + "wide": { + "message": "Eni" + }, + "extraWide": { + "message": "Ekstra enli" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 5ff88dce3a..b09e7374c6 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -20,16 +20,16 @@ "message": "Стварыць уліковы запіс" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Упершыню ў Bitwarden?" }, "logInWithPasskey": { "message": "Log in with passkey" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Выкарыстаць аднаразовы ўваход" }, "welcomeBack": { - "message": "Welcome back" + "message": "З вяртаннем" }, "setAStrongPassword": { "message": "Прызначыць надзейны пароль" @@ -120,7 +120,7 @@ "message": "Скапіяваць пароль" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Скапіяваць парольную фразу" }, "copyNote": { "message": "Скапіяваць нататку" @@ -153,13 +153,13 @@ "message": "Скапіяваць нумар ліцэнзіі" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Скапіяваць прыватны ключ" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Скапіяваць публічны ключ" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Скапіяваць адбітак пальца" }, "copyCustomField": { "message": "Скапіяваць $FIELD$", @@ -177,7 +177,7 @@ "message": "Скапіяваць нататкі" }, "fill": { - "message": "Fill", + "message": "Запоўніць", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -436,7 +436,7 @@ "message": "Генерыраваць пароль" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Згенерыраваць парольную фразу" }, "regeneratePassword": { "message": "Паўторна генерыраваць пароль" @@ -600,7 +600,7 @@ "message": "Адкрыць сайт" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Адкрыць сайт $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -855,7 +855,7 @@ "message": "Увайсці" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Увайсці ў Bitwarden" }, "restartRegistration": { "message": "Restart registration" @@ -1125,6 +1125,10 @@ "message": "ПАПЯРЭДЖАННЕ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Папярэджанне", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Пацвердзіць экспартаванне сховішча" }, @@ -1459,7 +1463,7 @@ "description": "Represents the message for allowing the user to enable the autofill overlay" }, "autofillSuggestionsSectionTitle": { - "message": "Autofill suggestions" + "message": "Прапановы аўтазапаўнення" }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Калі выяўлена форма ўваходу, то будзе выканана яе аўтазапаўненне падчас загрузкі вэб-старонкі." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Скампраметаваныя або ненадзейныя вэб-сайты могуць задзейнічаць функцыю аўтазапаўнення падчас загрузкі старонкі." }, @@ -1774,7 +1765,7 @@ "message": "Пасведчанне" }, "typeSshKey": { - "message": "SSH key" + "message": "Ключ SSH" }, "newItemHeader": { "message": "Новы $TYPE$", @@ -1807,7 +1798,7 @@ "message": "Гісторыя пароляў" }, "generatorHistory": { - "message": "Generator history" + "message": "Гісторыя генератара" }, "clearGeneratorHistoryTitle": { "message": "Clear generator history" @@ -1852,7 +1843,7 @@ "message": "Абароненыя нататкі" }, "sshKeys": { - "message": "SSH Keys" + "message": "Ключы SSH" }, "clear": { "message": "Ачысціць", @@ -1935,7 +1926,7 @@ "message": "Ачысціць гісторыю" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Адсутнічаюць элементы для паказу" }, "nothingGeneratedRecently": { "message": "You haven't generated anything recently" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 752372210b..652129f692 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -1125,6 +1125,10 @@ "message": "ВНИМАНИЕ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Внимание", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Потвърждаване на изнасянето на трезора" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "При засичане на формуляр за вписване при зареждането на уеб страницата автоматично да се попълват данните на съответстващата регистрация." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Внимание:$CLOSETAG$ Опасните или компроментирани уеб сайтове могат да се възползват от функционалността за автоматично попълване при зареждане на страницата.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Компроментирани и измамни уеб сайтове могат да се възползват от автоматичното попълване при зареждане на страницата." }, @@ -3288,16 +3279,16 @@ "message": "Отваря се в нов прозорец" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Запомняне на това устройство, така че в бъдеще вписването да бъде по-лесно" }, "deviceApprovalRequired": { "message": "Изисква се одобрение на устройството. Изберете начин за одобрение по-долу:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Необходимо е одобрение на устройството" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Изберете начин за одобряване по-долу" }, "rememberThisDevice": { "message": "Запомняне на това устройство" @@ -3373,7 +3364,7 @@ "message": "Липсва е-поща на потребителя" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Не е намерена е-поща на активен потребител. Ще бъдете отписан(а)." }, "deviceTrusted": { "message": "Устройството е доверено" @@ -3812,7 +3803,7 @@ "message": "Използване на" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Вписахте се!" }, "passkeyNotCopied": { "message": "Секретният ключ няма да бъде копиран" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Ширина на разширението" + }, + "wide": { + "message": "Широко" + }, + "extraWide": { + "message": "Много широко" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 18d214a37d..3b3b4cad43 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -1125,6 +1125,10 @@ "message": "সতর্কতা", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "ভল্ট রফতানির নিশ্চয়তা দিন" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "যদি কোনও লগইন ফর্ম সনাক্ত হয়, ওয়েব পৃষ্ঠাটি লোড হওয়ার পরে স্বয়ংক্রিয়ভাবে স্বতঃপূরণ করুন।" }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index a4a5f3b27e..a6e893887c 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index a1fc62182a..da8d7a3f8c 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1125,6 +1125,10 @@ "message": "ADVERTIMENT", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirma l'exportació de la Caixa forta" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Si es detecta un formulari d'inici de sessió, es realitza automàticament un emplenament automàtic quan es carrega la pàgina web." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Els llocs web compromesos o no fiables poden aprofitar-se de l'emplenament automàtic en carregar de la pàgina." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 03740f06ce..1984ba2700 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1125,6 +1125,10 @@ "message": "VAROVÁNÍ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Varování", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Potvrdit export trezoru" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Pokud je zjištěn přihlašovací formulář, automaticky se při načítání webové stránky vyplní přihlašovací údaje." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Varování:$CLOSETAG$ Kompromitované nebo nedůvěryhodné webové stránky mohou využívat automatické vyplňování při načítání stránky.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Kompromitované nebo nedůvěryhodné webové stránky mohou zneužívat automatické vyplňování při načítání stránky." }, @@ -3288,16 +3279,16 @@ "message": "Otevře se v novém okně" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamatovat si toto zařízení pro bezproblémové budoucí přihlášení" }, "deviceApprovalRequired": { "message": "Vyžaduje se schválení zařízení. Vyberte možnost schválení níže:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Vyžaduje se schválení zařízení" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Vyberte volbu schválení níže" }, "rememberThisDevice": { "message": "Zapamatovat toto zařízení" @@ -3373,7 +3364,7 @@ "message": "Chybí e-mail uživatele" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktivní uživatelský e-mail nebyl nalezen. Budete odhlášeni." }, "deviceTrusted": { "message": "Zařízení zařazeno mezi důvěryhodné" @@ -3812,7 +3803,7 @@ "message": "Přistupování" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Přihlášeno!" }, "passkeyNotCopied": { "message": "Přístupový klíč nebude zkopírován" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Šířka rozšíření" + }, + "wide": { + "message": "Šířka" + }, + "extraWide": { + "message": "Extra široký" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index fa175cbd19..c57db3b62f 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -1125,6 +1125,10 @@ "message": "RHYBUDD", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Llenwi'n awtomatig wrth i dudalen lwytho os canfyddir ffurflen mewngofnodi." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 12076e2827..8c10608a29 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1125,6 +1125,10 @@ "message": "ADVARSEL", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Advarsel", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Bekræft eksport af boks" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Registreres en loginformular, autoudfyld når websiden indlæses." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Advarsel:$CLOSETAG$ Kompromitterede eller ikke-betroede websteder kan misbruge autofyld ved sideindlæsning.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Kompromitterede eller ikke-betroede websteder kan misbruge autoudfyldning ved sideindlæsning." }, @@ -3288,16 +3279,16 @@ "message": "Åbnes i et nyt vindue" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Husk denne enhed for at gøre fremtidige indlogninger gnidningsløse" }, "deviceApprovalRequired": { "message": "Enhedsgodkendelse kræves. Vælg en godkendelsesmulighed nedenfor:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Enhedsgodkendelse kræves" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Vælg en godkendelsesmulighed nedenfor" }, "rememberThisDevice": { "message": "Husk denne enhed" @@ -3373,7 +3364,7 @@ "message": "Brugers e-mail mangler" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktiv bruger e-mail ikke fundet. Man logges ud." }, "deviceTrusted": { "message": "Enhed betroet" @@ -3812,7 +3803,7 @@ "message": "Tilgår" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Logget ind!" }, "passkeyNotCopied": { "message": "Adgangsnøglen kopieres ikke" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Udvidelsesbredde" + }, + "wide": { + "message": "Bred" + }, + "extraWide": { + "message": "Ekstra bred" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 9113150ab2..b03f5a9757 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1125,6 +1125,10 @@ "message": "ACHTUNG", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warnung", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Tresor-Export bestätigen" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Wenn eine Anmeldemaske erkannt wird, die Zugangsdaten automatisch ausfüllen während die Webseite lädt." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warnung:$CLOSETAG$ Kompromittierte oder nicht vertrauenswürdige Websites können Auto-Ausfüllen beim Laden einer Seite missbrauchen.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Kompromittierte oder nicht vertrauenswürdige Websites können Auto-Ausfüllen beim Laden der Seite ausnutzen." }, @@ -1852,7 +1843,7 @@ "message": "Sichere Notizen" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH-Schlüssel" }, "clear": { "message": "Löschen", @@ -2893,7 +2884,7 @@ "message": "E-Mail generieren" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Wert muss zwischen $MIN$ und $MAX$ liegen.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2907,7 +2898,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Verwende $RECOMMENDED$ oder mehr Zeichen, um ein starkes Passwort zu generieren.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2917,7 +2908,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Verwende $RECOMMENDED$ oder mehr Wörter, um eine starke Passphrase zu generieren.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -3174,7 +3165,7 @@ "message": "Benachrichtigung erneut senden" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Alle Anmeldeoptionen anzeigen" }, "viewAllLoginOptions": { "message": "Alle Anmeldeoptionen anzeigen" @@ -3183,16 +3174,16 @@ "message": "Eine Benachrichtigung wurde an dein Gerät gesendet." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Eine Benachrichtigung wurde an dein Gerät gesendet" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Stelle sicher, dass dein Konto entsperrt ist und die Fingerabdruck-Phrase mit der vom anderen Gerät übereinstimmt" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Du wirst benachrichtigt, sobald die Anfrage genehmigt wurde" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Brauchst du eine andere Option?" }, "loginInitiated": { "message": "Anmeldung eingeleitet" @@ -3288,16 +3279,16 @@ "message": "Wird in einem neuen Fenster geöffnet" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Dieses Gerät merken, um zukünftige Anmeldungen reibungslos zu gestalten" }, "deviceApprovalRequired": { "message": "Geräte-Genehmigung erforderlich. Wähle unten eine Genehmigungsoption aus:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Geräte-Genehmigung erforderlich" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Wähle unten eine Genehmigungsoption aus" }, "rememberThisDevice": { "message": "Dieses Gerät merken" @@ -3373,7 +3364,7 @@ "message": "E-Mail-Adresse des Benutzers fehlt" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktive Benutzer-E-Mail-Adresse nicht gefunden. Du wirst abgemeldet." }, "deviceTrusted": { "message": "Gerät wird vertraut" @@ -3812,7 +3803,7 @@ "message": "Zugriff auf" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Angemeldet!" }, "passkeyNotCopied": { "message": "Passkey wird nicht kopiert" @@ -4306,13 +4297,13 @@ "message": "Filter" }, "filterVault": { - "message": "Filter vault" + "message": "Tresor filtern" }, "filterApplied": { - "message": "One filter applied" + "message": "Ein Filter angewendet" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ Filter angewendet", "placeholders": { "count": { "content": "$1", @@ -4893,9 +4884,18 @@ "message": "Generiertes Passwort" }, "compactMode": { - "message": "Compact mode" + "message": "Kompaktmodus" }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Breite der Erweiterung" + }, + "wide": { + "message": "Breit" + }, + "extraWide": { + "message": "Extra breit" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index c333bf24f6..f56db1424a 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1125,6 +1125,10 @@ "message": "ΠΡΟΕΙΔΟΠΟΙΗΣΗ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Επιβεβαίωση εξαγωγής θησαυ/κίου" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Εάν εντοπιστεί φόρμα σύνδεσης, θα συμπληρωθεί αυτόματα κατά τη φόρτωση της σελίδας." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Προειδοποίηση:$CLOSETAG$ Οι παραβιασμένες ή μη αξιόπιστες ιστοσελίδες μπορούν να εκμεταλλευτούν την αυτόματη συμπλήρωση κατά τη φόρτωση της σελίδας.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Παραβιασμένοι ή μη αξιόπιστοι ιστότοποι μπορούν να εκμεταλλευτούν την αυτόματη συμπλήρωση κατά τη φόρτωση της σελίδας." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 5200cf81d0..39bc6ed9b8 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1315,6 +1319,12 @@ "enterVerificationCodeApp": { "message": "Enter the 6 digit verification code from your authenticator app." }, + "authenticationTimeout": { + "message": "Authentication timeout" + }, + "authenticationSessionTimedOut": { + "message": "The authentication session timed out. Please restart the login process." + }, "enterVerificationCodeEmail": { "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", "placeholders": { @@ -1503,19 +1513,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4894,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 30b04e63dc..392c8ef7f8 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, auto-fill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit auto-fill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 56ed22bfd0..5299693987 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm Vault Export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, automatically perform an auto-fill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit auto-fill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 13e4d9b003..a7e7b8cebf 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1125,6 +1125,10 @@ "message": "ADVERTENCIA", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Advertencia", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirma la exportación de la caja fuerte" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Si se detecta un formulario, realizar automáticamente un autorellenado cuando la web cargue." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Los sitios web vulnerados o no confiables pueden explotar el autorelleno al cargar la página." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index f7e3f7b135..020f282fb4 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -20,16 +20,16 @@ "message": "Konto loomine" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Esimene kord?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Logi sisse pääsuvõtmega" }, "useSingleSignOn": { "message": "Use single sign-on" }, "welcomeBack": { - "message": "Welcome back" + "message": "Tere tulemast tagasi" }, "setAStrongPassword": { "message": "Määra tugev parool" @@ -84,7 +84,7 @@ "message": "Liitu organisatsiooniga" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Liitu $ORGANIZATIONNAME$ organisatsiooniga", "placeholders": { "organizationName": { "content": "$1", @@ -1125,6 +1125,10 @@ "message": "HOIATUS", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Hoidla eksportimise kinnitamine" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Sisselogimise vormi tuvastamisel sisestatakse sinna kontoandmed automaatselt." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Häkitud või ebausaldusväärsed veebilehed võivad lehe laadimisel automaatset sisestamist kuritarvitada." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index db86f97810..cf9054064d 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -1125,6 +1125,10 @@ "message": "KONTUZ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Baieztatu kutxa gotorra esportatzea" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Saio-hasierako formulario bat detektatzen bada, auto-bete webgunea kargatzen denean." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index b0ac5e86c3..9f7091b3ec 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -1125,6 +1125,10 @@ "message": "اخطار", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "برون ریزی گاوصندوق را تأیید کنید" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "اگر یک فرم ورودی شناسایی شد، وقتی صفحه وب بارگذاری شد، به صورت خودکار پر شود." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "وبسایتهای در معرض خطر یا نامعتبر میتوانند از پر کردن خودکار در بارگذاری صفحه سوء استفاده کنند." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 2a4edd9cd6..aa8adbb799 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1125,6 +1125,10 @@ "message": "VAROITUS", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Varoitus", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Vahvista holvin vienti" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Automaattinen täyttö suoritetaan sivun avautuessa, jos sivulla havaitaan kirjautumislomake." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Varoitus:$CLOSETAG$ Vaarantuneet tai epäluotettavat sivustot voivat hyväksikäyttää sivun avautuessa suoritettavaa automaattista täyttöä.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Vaarantuneet tai epäluotettavat sivustot voivat hyväksikäyttää sivun avautuessa suoritettavaa automaattista täyttöä." }, @@ -1852,7 +1843,7 @@ "message": "Salatut muistiot" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH-avaimet" }, "clear": { "message": "Tyhjennä", @@ -3186,7 +3177,7 @@ "message": "Laitteeseesi lähetettiin ilmoitus" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Varmista, että vahvistavan laitteen holvi on avattu ja että se näyttää saman tunnistelausekkeen" }, "youWillBeNotifiedOnceTheRequestIsApproved": { "message": "Sinulle ilmoitetaan, kun pyyntö on hyväksytty" @@ -3288,16 +3279,16 @@ "message": "Avautuu uudessa ikkunassa" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Muista tämä laite tehdäksesi tulevista kirjautumisista saumattomia" }, "deviceApprovalRequired": { "message": "Laitehyväksyntä vaaditaan. Valitse hyväksyntätapa alta:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Laitteen hyväksyntä vaaditaan" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Valitse hyväksyntävaihtoehto alla" }, "rememberThisDevice": { "message": "Muista tämä laite" @@ -3373,7 +3364,7 @@ "message": "Käyttäjän sähköpostiosoite puuttuu" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktiivista käyttäjän sähköpostiosoitetta ei löytynyt. Kirjaudutaan ulos." }, "deviceTrusted": { "message": "Laitteeseen luotettu" @@ -3812,7 +3803,7 @@ "message": "Avataan" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Kirjautuminen onnistui!" }, "passkeyNotCopied": { "message": "Pääsyavainta ei kopioida" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index ab067bc9b8..fe3714b6d5 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -1125,6 +1125,10 @@ "message": "BABALA", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Tanggapin ang pag-export ng vault" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Kung natukoy ang isang form sa pag login, awtomatikong punan kapag naglo load ang web page." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Ang mga nakompromiso o hindi pinagkakatiwalaang mga website ay maaaring samantalahin ang awtomatikong pagpuno sa pag load ng pahina." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index f2e0658474..a4f238705a 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1125,6 +1125,10 @@ "message": "AVERTISSEMENT", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Avertissement", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirmer l'export du coffre" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Si un formulaire de connexion est détecté, il sera saisi automatiquement lors du chargement de la page web." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Attention :$CLOSETAG$ Les sites web compromis ou non fiables peuvent exploiter la saisie automatique lors du chargement de la page.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "les sites web compromis ou non fiables peuvent exploiter la saisie automatique au chargement de la page." }, @@ -1852,7 +1843,7 @@ "message": "Notes sécurisées" }, "sshKeys": { - "message": "SSH Keys" + "message": "Clés SSH" }, "clear": { "message": "Effacer", @@ -3288,16 +3279,16 @@ "message": "S'ouvre dans une nouvelle fenêtre" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Mémorisez cet appareil pour faciliter les futures connexions" }, "deviceApprovalRequired": { "message": "L'approbation de l'appareil est requise. Sélectionnez une option d'approbation ci-dessous :" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Autorisation de l'appareil requise" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Sélectionnez une option d'approbation ci-dessous" }, "rememberThisDevice": { "message": "Se souvenir de cet appareil" @@ -3373,7 +3364,7 @@ "message": "Courriel de l'utilisateur manquant" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Courriel utilisateur actif introuvable. Déconnexion en cours." }, "deviceTrusted": { "message": "Appareil de confiance" @@ -3812,7 +3803,7 @@ "message": "Accès en cours" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Connecté !" }, "passkeyNotCopied": { "message": "La clé d'identification (passkey) ne sera pas copiée" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Largeur de l'extension" + }, + "wide": { + "message": "Large" + }, + "extraWide": { + "message": "Très large" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index d1697b4dc5..845ddb08a9 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -1125,6 +1125,10 @@ "message": "ADVERTENCIA", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Advertencia", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 692d6f166d..88264c26e5 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -1125,6 +1125,10 @@ "message": "אזהרה", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "אישור ייצוא כספת" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "אם זוהה טופס כניסה, בצע אוטומטית מילוי-אוטומטי כשהעמוד נטען." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 050a4021a5..0d4cf1548d 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -1125,6 +1125,10 @@ "message": "चेतावनी", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "वॉल्ट निर्यात की पुष्टि करें" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "यदि लॉगिन फॉर्म का पता चलता है, तो वेब पेज लोड होने पर स्वचालित रूप से ऑटो-फिल करें।" }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index ec13925045..659a8d1b59 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -1125,6 +1125,10 @@ "message": "UPOZORENJE", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Potvrdi izvoz trezora" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Nakon učitavanja web stranice, ako je otkriven obrazac za prijavu, auto-ispuni." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Upozorenje:$CLOSETAG$ Ugrožene ili nepouzdane web stranice mogu iskoristiti auto-ispunu prilikom učitavanja stranice.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Ugrožene ili nepouzdane web stranice mogu iskoristiti auto-ispunu prilikom učitavanja stranice." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 4c42fc4f45..d11b62b6cb 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -1125,6 +1125,10 @@ "message": "FIGYELEM", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Figyelmeztetés", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Széf exportálás megerősítése" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Ha egy bejelentkezési űrlap észlelésre került, az adatok automatikus kitöltése az oldal betöltésekor." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Az oldalbetöltésnél automatikus kitöltést a feltört vagy nem megbízhatató weboldalak kihasználhatják." }, @@ -3288,16 +3279,16 @@ "message": "Megnyitás új ablakban" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Emlékezés az eszközre, hogy zökkenőmentes legyen a jövőbeni bejelentkezés" }, "deviceApprovalRequired": { "message": "Az eszköz jóváhagyása szükséges. Válasszunk egy jóváhagyási lehetőséget lentebb:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Eszköz jóváhagyás szükséges" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Válasszunk lentebb egy jóváhagyási lehetőséget." }, "rememberThisDevice": { "message": "Eszköz megjegyzése" @@ -3373,7 +3364,7 @@ "message": "A felhasználói email cím hiányzik." }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Az aktív felhasználói email cím nem található. Jelentkezzünk ki." }, "deviceTrusted": { "message": "Az eszköz megbízható." @@ -3812,7 +3803,7 @@ "message": "Elérés" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Megtörtént a bejelentkezés." }, "passkeyNotCopied": { "message": "A hozzáférési kulcs nem kerül másolásra." @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Béta" + }, + "extensionWidth": { + "message": "Kiterjesztés szélesség" + }, + "wide": { + "message": "Széles" + }, + "extraWide": { + "message": "Extra széles" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 25d3e1cdd9..467492a2f7 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -20,16 +20,16 @@ "message": "Buat Akun" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Baru menggunakan Bitwarden?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Masuk dengan kunci sandi" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Gunakan masuk tunggal" }, "welcomeBack": { - "message": "Welcome back" + "message": "Selamat datang kembali" }, "setAStrongPassword": { "message": "Atur sebuah kata sandi yang kuat" @@ -120,7 +120,7 @@ "message": "Salin Kata Sandi" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Salin frasa sandi" }, "copyNote": { "message": "Salin Catatan" @@ -153,13 +153,13 @@ "message": "Salin nomor lisensi" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Salin kunci pribadi" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Salin kunci publik" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Salin sidik jari" }, "copyCustomField": { "message": "Salin $FIELD$", @@ -177,7 +177,7 @@ "message": "Salin catatan" }, "fill": { - "message": "Fill", + "message": "Isikan", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -436,7 +436,7 @@ "message": "Buat Kata Sandi" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Buat frasa sandi" }, "regeneratePassword": { "message": "Buat Ulang Kata Sandi" @@ -600,7 +600,7 @@ "message": "Buka situs web" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Buka situs web $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -855,7 +855,7 @@ "message": "Masuk" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Masuk ke Bitwarden" }, "restartRegistration": { "message": "Mulai ulang pendaftaran" @@ -1125,6 +1125,10 @@ "message": "PERINGATAN", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Peringatan", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Konfirmasi Ekspor Brankas" }, @@ -1433,7 +1437,7 @@ "message": "URL Server" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL server yang dihosting mandiri", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Jika formulir info masuk terdeteksi, secara otomatis melakukan pengisian otomatis ketika memuat laman web." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Peringatan:$CLOSETAG$ Situs web yang disusupi atau tidak terpercaya dapat memanfaatkan isi otomatis ketika halaman dimuat.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Situs web yang disusupi atau tidak terpercaya dapat memanfaatkan isi otomatis ketika halaman dimuat." }, @@ -1774,10 +1765,10 @@ "message": "Identitas" }, "typeSshKey": { - "message": "SSH key" + "message": "Kunci SSH" }, "newItemHeader": { - "message": "New $TYPE$", + "message": "$TYPE$ baru", "placeholders": { "type": { "content": "$1", @@ -1786,7 +1777,7 @@ } }, "editItemHeader": { - "message": "Edit $TYPE$", + "message": "Sunting $TYPE$", "placeholders": { "type": { "content": "$1", @@ -1795,7 +1786,7 @@ } }, "viewItemHeader": { - "message": "View $TYPE$", + "message": "Lihat $TYPE$", "placeholders": { "type": { "content": "$1", @@ -1807,13 +1798,13 @@ "message": "Riwayat Kata Sandi" }, "generatorHistory": { - "message": "Generator history" + "message": "Riwayat penghasil" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Bersihkan riwayat penghasil" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Jika Anda melanjutkan, semua daftar akan dihapus selamanya dari riwayat penghasil. Apakah Anda yakin ingin melanjutkan?" }, "back": { "message": "Kembali" @@ -1822,7 +1813,7 @@ "message": "Koleksi" }, "nCollections": { - "message": "$COUNT$ collections", + "message": "$COUNT$ koleksi", "placeholders": { "count": { "content": "$1", @@ -1852,7 +1843,7 @@ "message": "Catatan Aman" }, "sshKeys": { - "message": "SSH Keys" + "message": "Kunci SSH" }, "clear": { "message": "Bersihkan", @@ -1878,7 +1869,7 @@ "description": "Domain name. Ex. website.com" }, "baseDomainOptionRecommended": { - "message": "Base domain (recommended)", + "message": "Domain utama (disarankan)", "description": "Domain name. Ex. website.com" }, "domainName": { @@ -1932,13 +1923,13 @@ "message": "Tidak ada sandi yang dapat dicantumkan." }, "clearHistory": { - "message": "Clear history" + "message": "Bersihkan riwayat" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Tidak ada yang dapat ditampilkan" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Anda belum menghasilkan apapun akhir-akhir ini" }, "remove": { "message": "Hapus" @@ -1999,16 +1990,16 @@ "message": "Buka kunci dengan PIN" }, "setYourPinTitle": { - "message": "Set PIN" + "message": "Atur PIN" }, "setYourPinButton": { - "message": "Set PIN" + "message": "Atur PIN" }, "setYourPinCode": { "message": "Setel kode PIN Anda untuk membuka kunci Bitwarden. Pengaturan PIN Anda akan diatur ulang jika Anda pernah keluar sepenuhnya dari aplikasi." }, "setYourPinCode1": { - "message": "Your PIN will be used to unlock Bitwarden instead of your master password. Your PIN will reset if you ever fully log out of Bitwarden." + "message": "PIN Anda akan digunakan untuk membuka Bitwarden alih-alih dengan kata sandi utama Anda. PIN Anda akan diatur ulang apabila Anda pernah keluar sepenuhnya dari Bitwarden." }, "pinRequired": { "message": "Membutuhkan kode PIN." @@ -2017,13 +2008,13 @@ "message": "Kode PIN tidak valid." }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "Too many invalid PIN entry attempts. Logging out." + "message": "Terlalu banyak usaha memasukkan PIN yang gagal. Mengeluarkan dari sesi." }, "unlockWithBiometrics": { "message": "Buka kunci dengan biometrik" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "Buka dengan kata sandi utama" }, "awaitDesktop": { "message": "Menunggu konfirmasi dari desktop" @@ -2035,7 +2026,7 @@ "message": "Kunci dengan kata sandi utama saat peramban dimulai ulang" }, "lockWithMasterPassOnRestart1": { - "message": "Require master password on browser restart" + "message": "Memerlukan kata sandi utama ketika mulai ulang peramban" }, "selectOneCollection": { "message": "Anda harus memilih setidaknya satu koleksi." @@ -2050,33 +2041,33 @@ "message": "Satu atau lebih kebijakan organisasi mempengaruhi pengaturan pembuat sandi Anda." }, "passwordGenerator": { - "message": "Password generator" + "message": "Pembuat kata sandi" }, "usernameGenerator": { - "message": "Username generator" + "message": "Pembuat nama pengguna" }, "useThisPassword": { - "message": "Use this password" + "message": "Gunakan kata sandi ini" }, "useThisUsername": { - "message": "Use this username" + "message": "Gunakan nama pengguna ini" }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "Kata sandi aman berhasil dibuat! Jangan lupa untuk memperbarui kata sandi Anda di situs web." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "Gunakan penghasil", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "untuk membuat sebuah kata sandi unit yang kuat", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultTimeoutAction": { "message": "Tindakan Batas Waktu Brankas" }, "vaultTimeoutAction1": { - "message": "Timeout action" + "message": "Batas waktu tindakan" }, "lock": { "message": "Kunci", @@ -2105,7 +2096,7 @@ "message": "Item Yang Dipulihkan" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "Sudah memiliki akun?" }, "vaultTimeoutLogOutConfirmation": { "message": "Keluar akan menghapus semua akses ke brankas Anda dan membutuhkan otentikasi daring setelah periode batas waktu tertentu. Apakah Anda yakin ingin menggunakan pengaturan ini?" @@ -2117,7 +2108,7 @@ "message": "Isi Otomatis dan Simpan" }, "fillAndSave": { - "message": "Fill and save" + "message": "Isikan dan simpan" }, "autoFillSuccessAndSavedUri": { "message": "Item yang Diisi Otomatis dan URI Tersimpan" @@ -2126,16 +2117,16 @@ "message": "Item Terisi Otomatis" }, "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": "Peringatan: Ini adalah halaman HTTP yang tidak aman, dan setiap informasi yang Anda kirim dapat berpotensi terlihat dan diubah oleh orang lain. Login ini awalnya disimpan di halaman aman (HTTPS) " }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "Anda masih ingin mengisi login ini?" }, "autofillIframeWarning": { - "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to autofill anyway, or Cancel to stop." + "message": "Formulir dihosting oleh domain yang berbeda dari URI login yang telah Anda simpan. Pilih OK untuk tetap mengisi otomatis, atau Batalkan untuk menghentikan." }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "Untuk mencegah peringatan ini di masa depan, simpan URI ini, $HOSTNAME$, ke benda login Bitwarden untuk situs ini.", "placeholders": { "hostname": { "content": "$1", @@ -2147,10 +2138,10 @@ "message": "Atur Kata Sandi Utama" }, "currentMasterPass": { - "message": "Current master password" + "message": "Kata sandi utama saat ini" }, "newMasterPass": { - "message": "New master password" + "message": "Kata sandi utama baru" }, "confirmNewMasterPass": { "message": "Confirm new master password" @@ -3093,13 +3084,13 @@ "message": "Premium subscription required" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "Organisasi ditangguhkan." }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "Benda-benda di Organisasi yang ditangguhkan tidak dapat diakses. Hubungi pemilik Organisasi Anda untuk mendapatkan panduan." }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "Sedang masuk ke $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -3108,25 +3099,25 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Pengaturan telah disunting" }, "environmentEditedClick": { - "message": "Click here" + "message": "Tekan di sini" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "untuk mengatur ulang ke pengaturan awal" }, "serverVersion": { - "message": "Server version" + "message": "Versi server" }, "selfHostedServer": { - "message": "self-hosted" + "message": "dihosting mandiri" }, "thirdParty": { - "message": "Third-party" + "message": "Pihak ketiga" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "Tersambung ke penerapan server pihak ketiga, $SERVERNAME$. Mohon pastikan bug menggunakan server resmi, atau laporkan mereka ke server pihak ketiga.", "placeholders": { "servername": { "content": "$1", @@ -3135,7 +3126,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "terakhir terlihat pada: $DATE$", "placeholders": { "date": { "content": "$1", @@ -3144,82 +3135,82 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Masuk dengan kata sandi utama" }, "loggingInAs": { - "message": "Logging in as" + "message": "Masuk sebagai" }, "notYou": { - "message": "Not you?" + "message": "Bukan Anda?" }, "newAroundHere": { - "message": "New around here?" + "message": "Baru di sini?" }, "rememberEmail": { "message": "Ingat email" }, "loginWithDevice": { - "message": "Log in with device" + "message": "Masuk dengan perangkat" }, "loginWithDeviceEnabledInfo": { - "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" + "message": "Masuk dengan perangkat harus diatur di pengaturan aplikasi Bitwarden. Perlu pilihan lainnya?" }, "fingerprintPhraseHeader": { - "message": "Fingerprint phrase" + "message": "Frasa sidik jari" }, "fingerprintMatchInfo": { - "message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device." + "message": "Pastikan brankas Anda terbuka dan frasa sidik jari cocok pada perangkat lainnya." }, "resendNotification": { - "message": "Resend notification" + "message": "Kirim ulang pemberitahuan" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Lihat semua pilihan masuk" }, "viewAllLoginOptions": { - "message": "View all log in options" + "message": "Lihat semua pilihan masuk" }, "notificationSentDevice": { - "message": "A notification has been sent to your device." + "message": "Sebuah pemberitahuan dikirim ke perangkat Anda." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Sebuah pemberitahuan telah dikirim ke perangkat Anda" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Pastikan akun Anda terbuka dan frasa sidik jari cocok pada perangkat lainnya" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Anda akan diberitahu setelah permintaan disetujui" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Perlu pilihan lainnya?" }, "loginInitiated": { - "message": "Login initiated" + "message": "Memulai login" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "Kata Sandi Utama yang Terpapar" }, "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": "Kata sandi ditemukan di pelanggaran data. Gunakan kata sandi unik untuk melindungi akun Anda. Apakah Anda yakin ingin menggunakan kata sandi yang terpapar?" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "Kata Sandi Utama yang Lemah dan Terpapar" }, "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": "Kata sandi lemah dikenali dan ditemukan di pelanggaran data. Gunakan kata sandi unik untuk melindungi akun Anda. Apakah Anda yakin ingin menggunakan kata sandi ini?" }, "checkForBreaches": { - "message": "Check known data breaches for this password" + "message": "Periksa pelanggaran data yang diketahui untuk kata sandi ini" }, "important": { - "message": "Important:" + "message": "Penting:" }, "masterPasswordHint": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "Kata sandi utama Anda tidak dapat dipulihkan jika Anda melupakannya!" }, "characterMinimum": { - "message": "$LENGTH$ character minimum", + "message": "Minimal $LENGTH$ karakter", "placeholders": { "length": { "content": "$1", @@ -3228,13 +3219,13 @@ } }, "autofillPageLoadPolicyActivated": { - "message": "Your organization policies have turned on autofill on page load." + "message": "Kebijakan organisasi Anda telah menyalakan isi otomatis ketika halaman dimuat." }, "howToAutofill": { - "message": "How to autofill" + "message": "Bagaimana cara mengisi otomatis" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "Pilih sebuah benda dari layar ini, gunakan pintasan $COMMAND$, atau jelajahi pilihan lainnya di pengaturan.", "placeholders": { "command": { "content": "$1", @@ -3243,31 +3234,31 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "Pilih sebuah benda dari layar ini, atau jelajahi pilihan lainnya di pengaturan." }, "gotIt": { - "message": "Got it" + "message": "Dimengerti" }, "autofillSettings": { - "message": "Autofill settings" + "message": "Pengaturan isi otomatis" }, "autofillKeyboardShortcutSectionTitle": { - "message": "Autofill shortcut" + "message": "Pintasan isi otomatis" }, "autofillKeyboardShortcutUpdateLabel": { - "message": "Change shortcut" + "message": "Ubah pintasan" }, "autofillKeyboardManagerShortcutsLabel": { - "message": "Manage shortcuts" + "message": "Kelola pintasan" }, "autofillShortcut": { - "message": "Autofill keyboard shortcut" + "message": "Pintasan papan ketik isi otomatis" }, "autofillLoginShortcutNotSet": { - "message": "The autofill login shortcut is not set. Change this in the browser's settings." + "message": "Pintasan isi otomatis login belum diatur. Ubah ini di pengaturan peramban." }, "autofillLoginShortcutText": { - "message": "The autofill login shortcut is $COMMAND$. Manage all shortcuts in the browser's settings.", + "message": "Pintasan isi otomatis login adalah $COMMAND$. Kelola semua pintasan di pengaturan peramban.", "placeholders": { "command": { "content": "$1", @@ -3276,7 +3267,7 @@ } }, "autofillShortcutTextSafari": { - "message": "Default autofill shortcut: $COMMAND$.", + "message": "Pintasan isi otomatis bawaan: $COMMAND$.", "placeholders": { "command": { "content": "$1", @@ -3285,7 +3276,7 @@ } }, "opensInANewWindow": { - "message": "Opens in a new window" + "message": "Buka di jendela baru" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { "message": "Remember this device to make future logins seamless" @@ -3478,16 +3469,16 @@ "message": "-- Type to filter --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Mengambil pilihan..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Tidak ada benda yang ditemukan" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Bersihkan semua" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ lagi", "placeholders": { "quantity": { "content": "$1", @@ -3499,165 +3490,165 @@ "message": "Submenu" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Saklar lipat", "description": "Toggling an expand/collapse state." }, "filelessImport": { - "message": "Import your data to Bitwarden?", + "message": "Impor data Anda ke Bitwarden?", "description": "Default notification title for triggering a fileless import." }, "lpFilelessImport": { - "message": "Protect your LastPass data and import to Bitwarden?", + "message": "Lindungi data LastPass Anda dan impor ke Bitwarden?", "description": "LastPass specific notification title for triggering a fileless import." }, "lpCancelFilelessImport": { - "message": "Save as unencrypted file", + "message": "Simpan sebagai berkas yang tidak dienkripsi", "description": "LastPass specific notification button text for cancelling a fileless import." }, "startFilelessImport": { - "message": "Import to Bitwarden", + "message": "Impor ke Bitwarden", "description": "Notification button text for starting a fileless import." }, "importing": { - "message": "Importing...", + "message": "Mengimpor...", "description": "Notification message for when an import is in progress." }, "dataSuccessfullyImported": { - "message": "Data successfully imported!", + "message": "Data berhasil diimpor!", "description": "Notification message for when an import has completed successfully." }, "dataImportFailed": { - "message": "Error importing. Check console for details.", + "message": "Gagal mengimpor. Periksa konsol untuk rinciannya.", "description": "Notification message for when an import has failed." }, "importNetworkError": { - "message": "Network error encountered during import.", + "message": "Kesalahan jaringan ditemui ketika mengimpor.", "description": "Notification message for when an import has failed due to a network error." }, "aliasDomain": { - "message": "Alias domain" + "message": "Domain alias" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be autofilled on page load. Autofill on page load turned off.", + "message": "Benda dengan meminta ulang kata sandi utama tidak dapat diisikan otomatis ketika halaman dimuat. Isi otomatis ketika halaman dimuat dimatikan.", "description": "Toast message for describing that master password re-prompt cannot be autofilled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Autofill on page load set to use default setting.", + "message": "Isi otomatis ketika halaman dimuat telah diatur untuk menggunakan pengaturan bawaan.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "Matikan minta ulang kata sandi utama untuk menyunting kolom ini", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, "toggleSideNavigation": { - "message": "Toggle side navigation" + "message": "Saklar bilah isi navigasi" }, "skipToContent": { - "message": "Skip to content" + "message": "Loncat ke konten" }, "bitwardenOverlayButton": { - "message": "Bitwarden autofill menu button", + "message": "Tombol menu isi otomatis Bitwarden", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Toggle Bitwarden autofill menu", + "message": "Saklar menu isi otomatis Bitwarden", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden autofill menu", + "message": "Menu isi otomatis Bitwarden", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { - "message": "Unlock your account to view matching logins", + "message": "Buka akun Anda untuk melihat login yang cocok", "description": "Text to display in overlay when the account is locked." }, "unlockYourAccountToViewAutofillSuggestions": { - "message": "Unlock your account to view autofill suggestions", + "message": "Buka akun Anda untuk melihat saran isi otomatis", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Unlock account", + "message": "Buka akun", "description": "Button text to display in overlay when the account is locked." }, "unlockAccountAria": { - "message": "Unlock your account, opens in a new window", + "message": "Buka akun Anda, membukanya di jendela baru", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "fillCredentialsFor": { - "message": "Fill credentials for", + "message": "Isi tanda pengenal untuk", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "Nama pengguna sebagian", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "Tidak ada benda untuk ditampilkan", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "New item", + "message": "Benda baru", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "Tambah benda brankas baru", "description": "Screen reader text (aria-label) for new item button in overlay" }, "newLogin": { - "message": "New login", + "message": "Login baru", "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { - "message": "Add new vault login item, opens in a new window", + "message": "Tambah baru untuk benda login, membukanya di jendela baru", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { - "message": "New card", + "message": "Kartu baru", "description": "Button text to display within inline menu when there are no matching items on a credit card field" }, "addNewCardItemAria": { - "message": "Add new vault card item, opens in a new window", + "message": "Tambah brankas baru untuk benda kartu, membukanya di jendela baru", "description": "Screen reader text (aria-label) for new card button within inline menu" }, "newIdentity": { - "message": "New identity", + "message": "Pengenal baru", "description": "Button text to display within inline menu when there are no matching items on an identity field" }, "addNewIdentityItemAria": { - "message": "Add new vault identity item, opens in a new window", + "message": "Tambah brankas baru untuk benda pengenal, membukanya di jendela baru", "description": "Screen reader text (aria-label) for new identity button within inline menu" }, "bitwardenOverlayMenuAvailable": { - "message": "Bitwarden autofill menu available. Press the down arrow key to select.", + "message": "Menu isi otomatis Bitwarden tersedia. Tekan tombol panah ke bawah untuk memilih.", "description": "Screen reader text for announcing when the overlay opens on the page" }, "turnOn": { - "message": "Turn on" + "message": "Nyalakan" }, "ignore": { - "message": "Ignore" + "message": "Abaikan" }, "importData": { - "message": "Import data", + "message": "Impor data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" }, "importError": { - "message": "Import error" + "message": "Kesalahan impor" }, "importErrorDesc": { - "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." + "message": "Ada masalah dengan data yang Anda coba impor. Harap selesaikan kesalahan yang tercantum di bawah ini pada berkas sumber Anda dan coba lagi." }, "resolveTheErrorsBelowAndTryAgain": { - "message": "Resolve the errors below and try again." + "message": "Selesaikan masalah berikut dan coba lagi." }, "description": { - "message": "Description" + "message": "Keterangan" }, "importSuccess": { - "message": "Data successfully imported" + "message": "Data berhasil diimpor" }, "importSuccessNumberOfItems": { - "message": "A total of $AMOUNT$ items were imported.", + "message": "Sejumlah $AMOUNT$ benda telah diimpor.", "placeholders": { "amount": { "content": "$1", @@ -3666,10 +3657,10 @@ } }, "tryAgain": { - "message": "Try again" + "message": "Coba lagi" }, "verificationRequiredForActionSetPinToContinue": { - "message": "Verification required for this action. Set a PIN to continue." + "message": "Diperlukan verifikasi untuk tindakan ini. Atur PIN untuk melanjutkan." }, "setPin": { "message": "Set PIN" @@ -3842,152 +3833,152 @@ "message": "Search or save passkey as new login" }, "confirm": { - "message": "Confirm" + "message": "Konfirmasi" }, "savePasskey": { - "message": "Save passkey" + "message": "Simpan kunci sandi" }, "savePasskeyNewLogin": { - "message": "Save passkey as new login" + "message": "Simpan kunci sandi sebagai login baru" }, "chooseCipherForPasskeySave": { - "message": "Choose a login to save this passkey to" + "message": "Pilih sebuah login untuk menyimpan kunci sandi ini" }, "chooseCipherForPasskeyAuth": { - "message": "Choose a passkey to log in with" + "message": "Pilih sebuah kunci sandi untuk masuk" }, "passkeyItem": { - "message": "Passkey Item" + "message": "Benda kunci sandi" }, "overwritePasskey": { - "message": "Overwrite passkey?" + "message": "Timpa kunci sandi?" }, "overwritePasskeyAlert": { - "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + "message": "Benda ini telah memiliki kunci sandi. Apakah Anda yakin ingin menimpa kunci sandi yang sekarang?" }, "featureNotSupported": { - "message": "Feature not yet supported" + "message": "Kemampuan belum didukung" }, "yourPasskeyIsLocked": { - "message": "Authentication required to use passkey. Verify your identity to continue." + "message": "Otentikasi diperlukan untuk menggunakan kunci sandi. Verifikasikan diri Anda untuk melanjutkan." }, "multifactorAuthenticationCancelled": { - "message": "Multifactor authentication cancelled" + "message": "Otentikasi multifaktor dibatalkan" }, "noLastPassDataFound": { - "message": "No LastPass data found" + "message": "Tidak ada data LastPass yang ditemukan" }, "incorrectUsernameOrPassword": { - "message": "Incorrect username or password" + "message": "Nama pengguna atau kata sandi salah" }, "incorrectPassword": { - "message": "Incorrect password" + "message": "Kata sandi salah" }, "incorrectCode": { - "message": "Incorrect code" + "message": "Kode salah" }, "incorrectPin": { - "message": "Incorrect PIN" + "message": "PIN salah" }, "multifactorAuthenticationFailed": { - "message": "Multifactor authentication failed" + "message": "Otentikasi multifaktor gagal" }, "includeSharedFolders": { - "message": "Include shared folders" + "message": "Sertakan folder yang dibagikan" }, "lastPassEmail": { - "message": "LastPass Email" + "message": "Surel LastPass" }, "importingYourAccount": { - "message": "Importing your account..." + "message": "Mengimpor akun Anda..." }, "lastPassMFARequired": { - "message": "LastPass multifactor authentication required" + "message": "Otentikasi multifaktor LastPass diperlukan" }, "lastPassMFADesc": { - "message": "Enter your one-time passcode from your authentication app" + "message": "Masukkan kode sandi sekali-waktu dari aplikasi otentikator Anda" }, "lastPassOOBDesc": { - "message": "Approve the login request in your authentication app or enter a one-time passcode." + "message": "Setujui permintaan masuk di aplikasi otentikasi Anda atau masukkan kode sandi sekali-waktu." }, "passcode": { - "message": "Passcode" + "message": "Kode sandi" }, "lastPassMasterPassword": { - "message": "LastPass master password" + "message": "Kata sandi utama LastPass" }, "lastPassAuthRequired": { - "message": "LastPass authentication required" + "message": "Otentikasi LastPass diperlukan" }, "awaitingSSO": { - "message": "Awaiting SSO authentication" + "message": "Menunggu otentikasi SSO" }, "awaitingSSODesc": { - "message": "Please continue to log in using your company credentials." + "message": "Harap lanjutkan masuk menggunakan tanda pengenal perusahaan Anda." }, "seeDetailedInstructions": { - "message": "See detailed instructions on our help site at", + "message": "Lihat petunjuk lengkap pada situs bantuan kami di", "description": "This is followed a by a hyperlink to the help website." }, "importDirectlyFromLastPass": { - "message": "Import directly from LastPass" + "message": "Impor langsung dari LastPass" }, "importFromCSV": { - "message": "Import from CSV" + "message": "Impor dari CSV" }, "lastPassTryAgainCheckEmail": { - "message": "Try again or look for an email from LastPass to verify it's you." + "message": "Coba lagi atau cari surel dari LastPass untuk memverifikasi bahwa ini adalah Anda." }, "collection": { - "message": "Collection" + "message": "Koleksi" }, "lastPassYubikeyDesc": { - "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + "message": "Masukkan YubiKey yang ditautkan dengan akun LastPass Anda ke port USB komputer Anda, kemudian sentuh tombolnya." }, "switchAccount": { - "message": "Switch account" + "message": "Ganti akun" }, "switchAccounts": { - "message": "Switch accounts" + "message": "Ganti akun" }, "switchToAccount": { - "message": "Switch to account" + "message": "Ganti ke akun" }, "activeAccount": { - "message": "Active account" + "message": "Akun aktif" }, "availableAccounts": { - "message": "Available accounts" + "message": "Akun yang tersedia" }, "accountLimitReached": { - "message": "Account limit reached. Log out of an account to add another." + "message": "Batas akun tercapai. Keluar dari akun untuk menambahkan akun lain." }, "active": { - "message": "active" + "message": "aktif" }, "locked": { - "message": "locked" + "message": "terkunci" }, "unlocked": { - "message": "unlocked" + "message": "terbuka" }, "server": { "message": "server" }, "hostedAt": { - "message": "hosted at" + "message": "dihost di" }, "useDeviceOrHardwareKey": { - "message": "Use your device or hardware key" + "message": "Gunakan perangkat Anda atau kunci perangkat keras" }, "justOnce": { - "message": "Just once" + "message": "Hanya sekali" }, "alwaysForThisSite": { - "message": "Always for this site" + "message": "Selalu untuk situs ini" }, "domainAddedToExcludedDomains": { - "message": "$DOMAIN$ added to excluded domains.", + "message": "$DOMAIN$ ditambahkan ke domain yang dikecualikan.", "placeholders": { "domain": { "content": "$1", @@ -3996,7 +3987,7 @@ } }, "commonImportFormats": { - "message": "Common formats", + "message": "Format umum", "description": "Label indicating the most common import formats" }, "confirmContinueToBrowserSettingsTitle": { @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index fe3f7467b2..e11b793399 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -1125,6 +1125,10 @@ "message": "ATTENZIONE", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Conferma esportazione della cassaforte" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Se sono rilevati campi di login, riempili automaticamente quando la pagina si carica." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Attenzione:$CLOSETAG$ Siti Web compromessi o non attendibili possono sfruttare il riempimento automatico al caricamento della pagina.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Siti compromessi potrebbero sfruttare il riempimento automatico al caricamento della pagina." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index a1cb2877d8..d9908835c8 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1125,6 +1125,10 @@ "message": "警告", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "保管庫のエクスポートの確認" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "ページ読み込み時にログインフォームを検出したとき、ログイン情報を自動入力します。" }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$警告:$CLOSETAG$ 侵害された、または信頼できないウェブサイトは、ページ読み込み時の自動入力を悪用する可能性があります。", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "ウイルス感染したり信頼できないウェブサイトは、ページの読み込み時の自動入力を悪用できてしまいます。" }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 5a0dbf4f9e..8b92a9ff34 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -1125,6 +1125,10 @@ "message": "გაფრთხილება", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 0ae9d0617f..6f26673abc 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 660c8cac25..6d15ddc906 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -1125,6 +1125,10 @@ "message": "ಎಚ್ಚರಿಕೆ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "ವಾಲ್ಟ್ ರಫ್ತು ಖಚಿತಪಡಿಸಿ" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "ಲಾಗಿನ್ ಫಾರ್ಮ್ ಪತ್ತೆಯಾದಲ್ಲಿ, ವೆಬ್ ಪುಟ ಲೋಡ್ ಆಗುವಾಗ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸ್ವಯಂ ಭರ್ತಿ ಮಾಡಿ." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index e058e32efa..0b527149d1 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -1125,6 +1125,10 @@ "message": "경고", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "보관함 내보내기 확인" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "로그인 양식을 감지하면 웹 페이지 로드 시 자동 완성을 자동으로 수행합니다." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "취약하거나 신뢰할 수 없는 웹사이트 페이지 로드 시 자동 완성이 악용될 수 있습니다." }, @@ -4423,7 +4414,7 @@ } }, "enableAnimations": { - "message": "Enable animations" + "message": "애니메이션 활성화" }, "showAnimations": { "message": "Show animations" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index f1e8d4f223..3318bd8df1 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -1125,6 +1125,10 @@ "message": "ĮSPĖJIMAS", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Patvirtinti saugyklos eksportą" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Jei aptikta prisijungimo forma, automatiškai užpildyti, kai kraunamas tinklalapis." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Pažeistos arba nepatikimos svetainės gali išnaudoti automatinį užpildymą įkeliant puslapį." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 0ce6bf97a2..cb0f299dcb 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -1125,6 +1125,10 @@ "message": "UZMANĪBU", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Brīdinājums", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Apstiprināt glabātavas satura izgūšanu" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Ja tiek noteikta pieteikšanās veidne, tā tiks aizpildīta lapas ielādes brīdī." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Brīdinājums:$CLOSETAG$ pārveidotās vai neuzticamās tīmekļvietnēs automātiskā aizpilde lapas ielādes laikā var tikt ļaunprātīgi izmantota.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Pārveidotās vai neuzticamās tīmekļvietnēs automātiskā aizpilde lapas ielādes laikā var tikt ļaunprātīgi izmantota." }, @@ -1852,7 +1843,7 @@ "message": "Drošās piezīmes" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH atslēgas" }, "clear": { "message": "Notīrīt", @@ -2917,7 +2908,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Jāizmanto $RECOMMENDED$ vai vairāk vārdu, lai aizveidotu spēcīgu parles vārdkopu.", + "message": " Jāizmanto $RECOMMENDED$ vai vairāk vārdu, lai aizveidotu spēcīgu paroles vārdkopu.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -3288,16 +3279,16 @@ "message": "Atver jaunā logā" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Atcerēties šo ierīci, lai nākotnes pieteikšanos padarītu plūdenāku" }, "deviceApprovalRequired": { "message": "Nepieciešams ierīces apstiprinājums. Zemāk jāatlasa apstiprinājuma iespēja:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Nepieciešama ierīces apstiprināšana" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Zemāk jāatlasa apstiprināšnas iespēja" }, "rememberThisDevice": { "message": "Atcerēties šo ierīci" @@ -3373,7 +3364,7 @@ "message": "Trūkst lietotāja e-pasta adreses" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktīva lietotāja e-pasta adrese netika atrasta. Notiek atteikšanās." }, "deviceTrusted": { "message": "Ierīce ir uzticama" @@ -3812,7 +3803,7 @@ "message": "Piekļūst" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Pieteicies." }, "passkeyNotCopied": { "message": "Piekļuves atslēga netiks ievietota starpliktuvē" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Paplašinājuma platums" + }, + "wide": { + "message": "Plats" + }, + "extraWide": { + "message": "Ļoti plats" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index ba6a3cc9f2..f5a2e244fc 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -1125,6 +1125,10 @@ "message": "മുന്നറിയിപ്പ്", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "ഒരു ലോഗിൻ ഫോം കണ്ടെത്തിയാൽ, വെബ് പേജ് ലോഡുചെയ്യുമ്പോൾ യാന്ത്രികമായി ഒരു സ്വയം പൂരിപ്പിക്കൽ നടത്തുക." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 02e1fa16b7..bd7a1c755e 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 0ae9d0617f..6f26673abc 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index bf1312feb2..027923d050 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1125,6 +1125,10 @@ "message": "ADVARSEL", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Bekreft eksport av hvelvet" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Dersom et innloggingskjema blir oppdaget, utfør automatisk en auto-utfylling når nettstedet lastes inn." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Kompromitterte eller upålitelige nettsider kan utnytte auto-utfylling når du laster inn siden." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 0ae9d0617f..6f26673abc 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 5fddc8f069..6ed6627330 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1125,6 +1125,10 @@ "message": "WAARSCHUWING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Waarschuwing", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Kluisexport bevestigen" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Als een inlogformulier wordt gedetecteerd, dan worden de inloggegevens automatisch ingevuld." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Waarschuwing:$CLOSETAG$ Geconpromitteerde of niet-vertrouwde websites kunnen het automatische invullen bij het laden van de pagina misbruiken.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Gehackte of onbetrouwbare websites kunnen auto-invullen tijdens het laden van de pagina misbruiken." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extensiebreedte" + }, + "wide": { + "message": "Breed" + }, + "extraWide": { + "message": "Extra breed" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 0ae9d0617f..6f26673abc 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 0ae9d0617f..6f26673abc 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index bdfad0f7a7..dc5b84eb6e 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1125,6 +1125,10 @@ "message": "UWAGA", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Potwierdź eksportowanie sejfu" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Jeśli zostanie wykryty formularz logowania, automatycznie uzupełnij dane logowania po załadowaniu strony." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Ostrzeżenie:$CLOSETAG$ Niebezpieczne witryny są w stanie wykorzystać autouzupełnianie przy wczytywaniu strony.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Zaatakowane lub niezaufane witryny internetowe mogą wykorzystać funkcję autouzupełniania podczas wczytywania strony, aby wyrządzić szkody." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index d9ee8086a5..79c87bbda0 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -1125,6 +1125,10 @@ "message": "AVISO", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirmar Exportação do Cofre" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Se um formulário de login for detectado, realizar automaticamente um auto-preenchimento quando a página web carregar." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Aviso:$CLOSETAG$ Comprometido ou sites não confiáveis podem explorar o autopreenchimento ao carregar a página.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Sites comprometidos ou não confiáveis podem tomar vantagem do autopreenchimento ao carregar a página." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index f2498bdfff..46d0fee21c 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -1125,6 +1125,10 @@ "message": "AVISO", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Aviso", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirmar a exportação do cofre" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Se for detetado um formulário de início de sessão, o preenchimento automático é efetuado quando a página Web é carregada." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Aviso:$CLOSETAG$ Aviso: Os sites comprometidos ou não confiáveis podem explorar o preenchimento automático ao carregar a página.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Os sites comprometidos ou não confiáveis podem explorar o preenchimento automático ao carregar a página." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Largura da extensão" + }, + "wide": { + "message": "Ampla" + }, + "extraWide": { + "message": "Muito ampla" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 980341b49f..c2f145087c 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -1125,6 +1125,10 @@ "message": "AVERTISMENT", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirmare export seif" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Dacă se detectează un formular de autentificare, completați-l automat la încărcarea paginii web." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Site-urile web compromise sau nesigure pot profita de auto-completarea la încărcare." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 87b523cd89..7eb6d55cf5 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -1125,6 +1125,10 @@ "message": "ВНИМАНИЕ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Предупреждение", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Подтвердить экспорт хранилища" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Если обнаружена форма входа, автозаполнение выполняется при загрузке веб-страницы." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Предупреждение:$CLOSETAG$ взломанные или недоверенные сайты могут использовать автозаполнение при загрузке страницы.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Взломанные или недоверенные сайты могут внедрить вредоносный код во время автозаполнения при загрузке страницы." }, @@ -3288,16 +3279,16 @@ "message": "Откроется в новом окне" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Запомнить это устройство, чтобы в будущем авторизовываться быстрее" }, "deviceApprovalRequired": { "message": "Требуется одобрение устройства. Выберите вариант ниже:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Требуется подтверждение устройства" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Выберите вариант подтверждения ниже" }, "rememberThisDevice": { "message": "Запомнить это устройство" @@ -3373,7 +3364,7 @@ "message": "Отсутствует email пользователя" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Email активного пользователя не найден. Разлогиниваем." }, "deviceTrusted": { "message": "Доверенное устройство" @@ -3812,7 +3803,7 @@ "message": "Доступ" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Выполнен вход!" }, "passkeyNotCopied": { "message": "Passkey не будет скопирован" @@ -4662,7 +4653,7 @@ "message": "Теперь автозаполнение и поиск на вкладке Хранилище стали проще и интуитивно понятнее, чем когда-либо. Осмотритесь!" }, "accountActions": { - "message": "Действия с аккаунтом" + "message": "Действия аккаунта" }, "showNumberOfAutofillSuggestions": { "message": "Показывать количество вариантов автозаполнения логина на значке расширения" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Ширина расширения" + }, + "wide": { + "message": "Широкое" + }, + "extraWide": { + "message": "Очень широкое" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 4d1eeebc1f..42be772294 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -1125,6 +1125,10 @@ "message": "අවවාදයයි", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "සුරක්ෂිතාගාරය අපනයන තහවුරු" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "පිවිසුම් පෝරමයක් අනාවරණය කර ඇත්නම්, වෙබ් පිටුව පැටවුම් කරන විට ස්වයංක්රීයව ස්වයංක්රීයව පිරවීම සිදු කරන්න." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index d44be0f59c..9a720ea289 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1125,6 +1125,10 @@ "message": "UPOZORNENIE", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Upozornenie", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Potvrdiť export trezoru" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Ak je detekovaný prihlasovací formulár, automaticky vykonať vypĺňanie pri načítaní stránky." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Upozornenie:$CLOSETAG$ Kompromitované alebo nedôveryhodné webové stránky môžu využívať automatické vypĺňanie pri načítaní stránky.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Skompromitované alebo nedôveryhodné stránky môžu pri svojom načítaní zneužiť automatické dopĺňanie." }, @@ -3288,16 +3279,16 @@ "message": "Otvárať v novom okne" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamätať si toto zariadenie, pre budúce bezproblémové prihlásenie" }, "deviceApprovalRequired": { "message": "Vyžaduje sa schválenie zariadenia. Vyberte možnosť schválenia nižšie:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Vyžaduje sa schválenie zariadenia" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Vyberte možnosť schválenia nižšie" }, "rememberThisDevice": { "message": "Zapamätať si toto zariadenie" @@ -3373,7 +3364,7 @@ "message": "Chýba e-mail používateľa" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "E-mail aktívneho používateľa sa nenašiel. Odhlasuje sa." }, "deviceTrusted": { "message": "Dôveryhodné zariadenie" @@ -3812,7 +3803,7 @@ "message": "Pristupovanie" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Prihlásený!" }, "passkeyNotCopied": { "message": "Prístupový kód sa neskopíruje" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Šírka rozšírenia" + }, + "wide": { + "message": "Široké" + }, + "extraWide": { + "message": "Extra široké" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 2ec14a1c5a..b5dadd4340 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -1125,6 +1125,10 @@ "message": "OPOZORILO", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Potrdite izvoz trezorja" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Če Bitwarden na strani zazna prijavni obrazec, ga samodejno izpolni takoj, ko se stran naloži." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Spletne strani, ki jim ne zaupate ali v katere so vdrli, lahko zlorabijo samodejno izpolnjevanje ob naložitvi strani." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 112ba3ca3f..e7759897a8 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1125,6 +1125,10 @@ "message": "УПОЗОРЕЊЕ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Потврдите извоз сефа" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Ако се открије образац за пријаву, извршите аутоматско попуњавање када се веб страница учита." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Пажња:$CLOSETAG$ Компромитоване или непоуздане веб локације могу да искористе ауто-попуњавање при учитавању странице.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Компромитоване или непоуздане веб локације могу да искористе ауто-пуњење при учитавању странице." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 96bcac046f..2bbf1e133e 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -1125,6 +1125,10 @@ "message": "VARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Bekräfta export av valv" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Utför automatisk ifyllnad om ett inloggningsformulär upptäcks när webbsidan laddas." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Komprometterade eller ej betrodda webbplatser kan utnyttja automatisk ifyllnad vid sidladdning." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 0ae9d0617f..6f26673abc 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -1125,6 +1125,10 @@ "message": "WARNING", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Confirm vault export" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 540513662f..76d3e0cf54 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -1125,6 +1125,10 @@ "message": "คำเตือน", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "ยืนยันการส่งออกตู้นิรภัย" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, autofill when the web page loads." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Compromised or untrusted websites can exploit autofill on page load." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index eebefd2bd2..1d54e9865b 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -1125,6 +1125,10 @@ "message": "UYARI", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Uyarı", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Kasayı dışa aktarmayı onaylayın" }, @@ -1274,7 +1278,7 @@ "message": "Bitwarden'ı desteklediğiniz için teşekkür ederiz." }, "premiumFeatures": { - "message": "Upgrade to Premium and receive:" + "message": "Premium'a yükseltin ve şunları alın:" }, "premiumPrice": { "message": "Bunların hepsi sadece yılda $PRICE$!", @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Sayfa yüklendiğinde giriş formu tespit edilirse otomatik olarak formu doldur." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Uyarı:$CLOSETAG$ Ele geçirilmiş veya güvenilmeyen web siteleri sayfa yüklenirken otomatik doldurmayı suistimal edebilir.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Ele geçirilmiş veya güvenilmeyen web siteleri sayfa yüklenirken otomatik doldurmayı suistimal edebilir." }, @@ -3288,16 +3279,16 @@ "message": "Yeni pencerede açılır" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Sonraki girişleri kolaylaştırmak için bu cihazı hatırla" }, "deviceApprovalRequired": { "message": "Cihaz onayı gerekiyor. Lütfen onay yönteminizi seçin:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Cihazı onaylamanız gerekiyor" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Aşağıdan bir onay yöntemi seçin" }, "rememberThisDevice": { "message": "Bu cihazı hatırla" @@ -3373,7 +3364,7 @@ "message": "Kullanıcının e-postası eksik" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktif kullanıcı e-postası bulunamadı. Çıkış yapılıyor." }, "deviceTrusted": { "message": "Cihaza güvenildi" @@ -3809,10 +3800,10 @@ "message": "Geçiş anahtarı" }, "accessing": { - "message": "Accessing" + "message": "Erişim" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Giriş yapıldı!" }, "passkeyNotCopied": { "message": "Geçiş anahtarı kopyalanmayacak" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Uzantı genişliği" + }, + "wide": { + "message": "Geniş" + }, + "extraWide": { + "message": "Ekstra geniş" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 914541bb4a..3ffc1f375f 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -1125,6 +1125,10 @@ "message": "ПОПЕРЕДЖЕННЯ", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Підтвердити експорт сховища" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Якщо виявлено форму входу, автоматично заповнювати її під час завантаження вебсторінки." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Обережно!$CLOSETAG$ Скомпрометовані або ненадійні вебсайти можуть використати функцію автозаповнення під час завантаження сторінки для завдання шкоди.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Скомпрометовані або ненадійні вебсайти можуть використати функцію автозаповнення під час завантаження сторінки для завдання шкоди." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Бета" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 16120158b0..88adbc3a53 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -1125,6 +1125,10 @@ "message": "CẢNH BÁO", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "Xác nhận xuất kho" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "Nếu phát hiện biểu mẫu đăng nhập, thực hiện tự động điền khi trang web tải xong." }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Cảnh báo:$CLOSETAG$ Các trang web bị xâm phạm hoặc không đáng tin cậy có thể lợi dụng tính năng tự động điền khi trang web được tải.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "Các trang web bị xâm phạm hoặc không đáng tin cậy có thể khai thác tính năng tự động điền khi tải trang." }, @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 67211476ae..05b71990c1 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -1125,6 +1125,10 @@ "message": "警告", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "警告", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "确认密码库导出" }, @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "网页加载时如果检测到登录表单,则执行自动填充。" }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$警告:$CLOSETAG$不完整或不信任的网站可以利用页面加载时的自动填充功能。", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "不完整或不信任的网站可以利用页面加载时的自动填充功能。" }, @@ -1852,7 +1843,7 @@ "message": "安全笔记" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH 密钥" }, "clear": { "message": "清除", @@ -3288,16 +3279,16 @@ "message": "在新窗口中打开" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "记住此设备以便将来无缝登录" }, "deviceApprovalRequired": { "message": "需要设备批准。请在下面选择一个批准选项:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "需要设备批准" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "在下方选择一个批准选项" }, "rememberThisDevice": { "message": "记住此设备" @@ -3373,7 +3364,7 @@ "message": "缺少用户电子邮件" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "未找到活动的用户电子邮件。您将被注销。" }, "deviceTrusted": { "message": "设备已信任" @@ -3812,7 +3803,7 @@ "message": "正在访问" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "已登录!" }, "passkeyNotCopied": { "message": "通行密钥不会被复制" @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta 版" + }, + "extensionWidth": { + "message": "扩展宽度" + }, + "wide": { + "message": "宽" + }, + "extraWide": { + "message": "超宽" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index db169e503b..fd71e2de56 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -23,10 +23,10 @@ "message": "New to Bitwarden?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "使用密碼金鑰登入" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "使用單一登入" }, "welcomeBack": { "message": "Welcome back" @@ -241,7 +241,7 @@ "message": "請求密碼提示" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "輸入您的帳號電子郵件,您的密碼提示會傳送給您" + "message": "輸入您帳戶的電子郵件,您的密碼提示會傳送給您" }, "passwordHint": { "message": "密碼提示" @@ -274,7 +274,7 @@ "message": "變更主密碼" }, "continueToWebApp": { - "message": "接下來造訪 Web App 嗎?" + "message": "接下來造訪網頁 App 嗎?" }, "continueToWebAppDesc": { "message": "在 Web 應用程式上探索 Bitwarden 帳戶的更多功能。" @@ -327,7 +327,7 @@ "message": "Bitwarden 驗證器" }, "continueToAuthenticatorPageDesc": { - "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" + "message": "您可以使用 Bitwarden 驗證器儲存驗證器金鑰,並為兩步驟驗證流程產生 TOTP 代碼。前往 bitwarden.com 網站以了解更多資訊。" }, "bitwardenSecretsManager": { "message": "Bitwarden Secrets Manager" @@ -828,13 +828,13 @@ "message": "從目前網頁掃描驗證器二維碼" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "無縫兩步驟驗證" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden 可以儲存並填入兩步驟驗證碼。複製金鑰並貼上到此欄位。" }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden 可以儲存並填入兩步驟驗證碼。選擇相機圖示來截取此網站的驗證器QR code,或手動複製金鑰並貼上到此欄位。" }, "learnMoreAboutAuthenticators": { "message": "Learn more about authenticators" @@ -894,7 +894,7 @@ "message": "Make your account more secure by setting up two-step login in the Bitwarden web app." }, "twoStepLoginConfirmationTitle": { - "message": "Continue to web app?" + "message": "前往網頁 App 嗎?" }, "editedFolder": { "message": "資料夾已儲存" @@ -990,7 +990,7 @@ "message": "如果在您的密碼庫中找不到項目,則詢問是否新增項目。適用於所有已登入的帳戶。" }, "showCardsInVaultView": { - "message": "Show cards as Autofill suggestions on Vault view" + "message": "在密碼庫介面中顯示支付卡自動填入建議" }, "showCardsCurrentTab": { "message": "於分頁頁面顯示支付卡" @@ -999,7 +999,7 @@ "message": "於分頁頁面顯示信用卡以便於自動填入。" }, "showIdentitiesInVaultView": { - "message": "Show identities as Autofill suggestions on Vault view" + "message": "在密碼庫介面中顯示身分自動填入建議" }, "showIdentitiesCurrentTab": { "message": "於分頁頁面顯示身分" @@ -1089,7 +1089,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportFrom": { - "message": "Export from" + "message": "匯出自" }, "exportVault": { "message": "匯出密碼庫" @@ -1098,7 +1098,7 @@ "message": "檔案格式" }, "fileEncryptedExportWarningDesc": { - "message": "This file export will be password protected and require the file password to decrypt." + "message": "此檔案匯出將受密碼保護,需要檔案密碼才能解密。" }, "filePassword": { "message": "檔案密碼" @@ -1107,24 +1107,28 @@ "message": "此密碼將用於匯出和匯入此檔案" }, "accountRestrictedOptionDescription": { - "message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account." + "message": "使用衍生自您帳戶的使用者名稱與主密碼的加密金鑰,加密此匯出檔案,並限制只能匯入到目前的 Bitwarden 帳戶。" }, "passwordProtectedOptionDescription": { - "message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption." + "message": "設定一組檔案密碼來加密匯出的資料,並使用此密碼解密以匯入至任意 Bitwarden 帳戶。" }, "exportTypeHeading": { "message": "匯出類型" }, "accountRestricted": { - "message": "Account restricted" + "message": "帳號已受限制" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { - "message": "“File password” and “Confirm file password“ do not match." + "message": "「檔案密碼」與「確認檔案密碼」不一致。" }, "warning": { "message": "警告", "description": "WARNING (should stay in capitalized letters if the language permits)" }, + "warningCapitalized": { + "message": "警告", + "description": "Warning (should maintain locale-relevant capitalization)" + }, "confirmVaultExport": { "message": "確認匯出密碼庫" }, @@ -1144,7 +1148,7 @@ "message": "已共用" }, "bitwardenForBusinessPageDesc": { - "message": "Bitwarden for Business allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website." + "message": "Bitwarden 可讓您透過組織與其他人共用您的密碼庫項目。請造訪 bitwarden.com 網站以了解更多資訊。" }, "moveToOrganization": { "message": "移動至組織 " @@ -1205,7 +1209,7 @@ "message": "檔案" }, "fileToShare": { - "message": "File to share" + "message": "要分享的文件" }, "selectFile": { "message": "選取檔案" @@ -1241,7 +1245,7 @@ "message": "用於檔案附件的 1 GB 加密儲存空間。" }, "premiumSignUpEmergency": { - "message": "Emergency access." + "message": "緊急存取" }, "premiumSignUpTwoStepOptions": { "message": "專有的兩步驟登入選項,例如 YubiKey 和 Duo。" @@ -1265,7 +1269,7 @@ "message": "您可以在 bitwarden.com 網頁版密碼庫購買進階會員資格。現在要前往嗎?" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "您可以在 Bitwarden 網頁 App 的帳號設定中購買進階版。" }, "premiumCurrentMember": { "message": "您目前為進階會員!" @@ -1274,7 +1278,7 @@ "message": "感謝您支持 Bitwarden 。" }, "premiumFeatures": { - "message": "Upgrade to Premium and receive:" + "message": "升級到進階版並接收:" }, "premiumPrice": { "message": "每年只需 $PRICE$!", @@ -1286,7 +1290,7 @@ } }, "premiumPriceV2": { - "message": "All for just $PRICE$ per year!", + "message": "每年只需 $PRICE$!", "placeholders": { "price": { "content": "$1", @@ -1503,19 +1507,6 @@ "enableAutoFillOnPageLoadDesc": { "message": "網頁載入時如果偵測到登入表單,則執行自動填入。" }, - "autofillOnPageLoadWarning": { - "message": "$OPENTAG$Warning:$CLOSETAG$ Compromised or untrusted websites can exploit autofill on page load.", - "placeholders": { - "openTag": { - "content": "$1", - "example": "" - }, - "closeTag": { - "content": "$2", - "example": "" - } - } - }, "experimentalFeature": { "message": "被入侵或不被信任的網站,可能會濫用頁面載入的自動填入功能。" }, @@ -2297,7 +2288,7 @@ "message": "Please unlock this user in the desktop application and try again." }, "biometricsNotAvailableTitle": { - "message": "Biometric unlock unavailable" + "message": "生物辨識解鎖不可用" }, "biometricsNotAvailableDesc": { "message": "Biometric unlock is currently unavailable. Please try again later." @@ -2330,7 +2321,7 @@ "message": "某個組織原則已禁止您將項目匯入至您的個人密碼庫。" }, "domainsTitle": { - "message": "Domains", + "message": "網域", "description": "A category title describing the concept of web domains" }, "excludedDomains": { @@ -2343,7 +2334,7 @@ "message": "對於所有已登入的帳戶,Bitwarden 不會詢問是否儲存這些網域的登入資訊。您必須重新整理頁面變更才會生效。" }, "websiteItemLabel": { - "message": "Website $number$ (URI)", + "message": "網站 $number$ (URI)", "placeholders": { "number": { "content": "$1", @@ -2361,17 +2352,17 @@ } }, "excludedDomainsSavedSuccess": { - "message": "Excluded domain changes saved" + "message": "例外網域更改已儲存" }, "limitSendViews": { - "message": "Limit views" + "message": "限制查看" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "在達到限額後,沒有人能查看此 Send。", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "剩餘 $ACCESSCOUNT$ 次查看次數", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2385,7 +2376,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDetails": { - "message": "Send details", + "message": "Send 詳細資訊", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { @@ -2400,7 +2391,7 @@ "message": "文字" }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "要分享的文字" }, "sendTypeFile": { "message": "檔案" @@ -2410,7 +2401,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "hideTextByDefault": { - "message": "Hide text by default" + "message": "默認隱藏文字" }, "maxAccessCountReached": { "message": "已達最大存取次數", @@ -2426,7 +2417,7 @@ "message": "密碼保護" }, "copyLink": { - "message": "Copy link" + "message": "複製連結" }, "copySendLink": { "message": "複製 Send 連結", @@ -2464,7 +2455,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "Are you sure you want to permanently delete this Send?", + "message": "您確定要永久刪除此 Send 嗎?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -2490,7 +2481,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletionDateDescV2": { - "message": "The Send will be permanently deleted on this date.", + "message": "此 Send 將在指定的日期後被永久刪除。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { @@ -2527,7 +2518,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "新增一個用於收件人存取此 Send 的可選密碼。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2572,15 +2563,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSendSuccessfully": { - "message": "Send created successfully!", + "message": "Send 創建成功!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHoursSingle": { - "message": "The Send will be available to anyone with the link for the next 1 hour.", + "message": "在接下來的 1 小時內,任何人都可以透過連結訪問此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHours": { - "message": "The Send will be available to anyone with the link for the next $HOURS$ hours.", + "message": "在接下來的 $HOURS$ 小時內,任何人都可以透過連結訪問此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "hours": { @@ -2590,11 +2581,11 @@ } }, "sendExpiresInDaysSingle": { - "message": "The Send will be available to anyone with the link for the next 1 day.", + "message": "在接下來的 1 天內,任何人都可以透過連結訪問此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInDays": { - "message": "The Send will be available to anyone with the link for the next $DAYS$ days.", + "message": "在接下來的 $DAYS$ 天內,任何人都可以透過連結訪問此 Send。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "days": { @@ -2604,7 +2595,7 @@ } }, "sendLinkCopied": { - "message": "Send link copied", + "message": "已複製 Send 連結", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { @@ -2612,11 +2603,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { - "message": "Pop out extension?", + "message": "彈出擴充程式?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { - "message": "To create a file Send, you need to pop out the extension to a new window.", + "message": "要建立檔案 Send,您需要彈出擴充程式到新視窗。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { @@ -2629,7 +2620,7 @@ "message": "要使用 Safari 來選擇檔案,請點選此橫幅彈出至新視窗。" }, "popOut": { - "message": "Pop out" + "message": "彈出" }, "sendFileCalloutHeader": { "message": "在開始之前" @@ -2665,7 +2656,7 @@ "message": "對收件人隱藏我的電子郵件地址。" }, "hideYourEmail": { - "message": "Hide your email address from viewers." + "message": "對查看者隱藏您的電子郵件地址。" }, "sendOptionsPolicyInEffect": { "message": "一個或多個組織原則正影響您的 Send 選項。" @@ -2683,7 +2674,7 @@ "message": "需要驗證電子郵件" }, "emailVerifiedV2": { - "message": "Email verified" + "message": "電子郵件已驗證" }, "emailVerificationRequiredDesc": { "message": "您必須驗證您的電子郵件才能使用此功能。您可以在網頁密碼庫裡驗證您的電子郵件。" @@ -2701,7 +2692,7 @@ "message": "您的主密碼不符合一個或多個組織政策規定。您必須立即更新您的主密碼才能存取密碼庫。進行此動作將登出您目前的工作階段,需要您重新登入。其他裝置上的工作階段可能持續長達一小時。" }, "tdeDisabledMasterPasswordRequired": { - "message": "Your organization has disabled trusted device encryption. Please set a master password to access your vault." + "message": "您的組織停用了信任裝置加密。若要存取您的密碼庫,請設定主密碼。" }, "resetPasswordPolicyAutoEnroll": { "message": "自動註冊" @@ -2725,7 +2716,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "$TOTAL$ 不足", "placeholders": { "total": { "content": "$1", @@ -2744,7 +2735,7 @@ "message": "分鐘" }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Enterprise policy requirements have been applied to your timeout options" + "message": "企業政策已套用至您的逾時選項中" }, "vaultTimeoutPolicyInEffect": { "message": "您的組織政策已限定您密碼庫逾時的時間長度。密碼庫逾時時間最高可以設定到 $HOURS$ 小時 $MINUTES$ 分鐘。", @@ -2760,7 +2751,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "最多 $HOURS$ 小時 $MINUTES$ 分鐘", "placeholders": { "hours": { "content": "$1", @@ -2773,7 +2764,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "message": "逾時時間超出了您組織設定的此限制:最多 $HOURS$ 小時 $MINUTES$ 分鐘", "placeholders": { "hours": { "content": "$1", @@ -2869,10 +2860,10 @@ } }, "exportingOrganizationVaultTitle": { - "message": "Exporting organization vault" + "message": "正在匯出組織密碼庫" }, "exportingOrganizationVaultDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", + "message": "只會匯出與 $ORGANIZATION$ 相關的組織密碼庫資料。不包括個人密碼庫和其他組織中的項目。", "placeholders": { "organization": { "content": "$1", @@ -2890,10 +2881,10 @@ "message": "產生使用者名稱" }, "generateEmail": { - "message": "Generate email" + "message": "生成電子郵件" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "數值必須在 $MIN$ 和 $MAX$ 之間。", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2907,7 +2898,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " 使用 $RECOMMENDED$ 或更多個字元產生更強的密碼。", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2917,7 +2908,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " 使用 $RECOMMENDED$ 或更多個單字產生強的密碼短語。", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2967,15 +2958,15 @@ "message": "使用外部轉寄服務產生一個電子郵件別名。" }, "forwarderDomainName": { - "message": "Email domain", + "message": "電子郵件網域", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "選擇一個所選服務支援的網域", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "$SERVICENAME$ error: $ERRORMESSAGE$", + "message": "$SERVICENAME$ 錯誤:$ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -2989,7 +2980,7 @@ } }, "forwarderGeneratedBy": { - "message": "Generated by Bitwarden.", + "message": "由 Bitwarden 生成。", "description": "Displayed with the address on the forwarding service's configuration screen." }, "forwarderGeneratedByWithWebsite": { @@ -3824,7 +3815,7 @@ "message": "發起站點需要驗證。沒有主密碼的帳戶尚未開通此功能。" }, "logInWithPasskeyQuestion": { - "message": "Log in with passkey?" + "message": "使用密碼金鑰登入?" }, "passkeyAlreadyExists": { "message": "用於這個應用程式的密碼金鑰已經存在。" @@ -3839,7 +3830,7 @@ "message": "No matching logins for this site" }, "searchSavePasskeyNewLogin": { - "message": "Search or save passkey as new login" + "message": "搜尋或將密碼金鑰儲存為新的登入資訊" }, "confirm": { "message": "確認" @@ -3851,10 +3842,10 @@ "message": "將密碼金鑰儲存為新的登入資訊" }, "chooseCipherForPasskeySave": { - "message": "Choose a login to save this passkey to" + "message": "選擇一個用於儲存此密碼金鑰的登入資訊" }, "chooseCipherForPasskeyAuth": { - "message": "Choose a passkey to log in with" + "message": "選擇一個用於登入的密碼金鑰" }, "passkeyItem": { "message": "密碼金鑰項目" @@ -4064,17 +4055,17 @@ "description": "Notification message for when updating credentials has succeeded." }, "saveCipherAttemptFailed": { - "message": "Error saving credentials. Check console for details.", + "message": "保存憑證時發生錯誤。檢查控制台以了解詳細資訊。", "description": "Notification message for when saving credentials has failed." }, "success": { "message": "Success" }, "removePasskey": { - "message": "Remove passkey" + "message": "移除密碼金鑰" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "密碼金鑰已移除" }, "autofillSuggestions": { "message": "Autofill suggestions" @@ -4221,7 +4212,7 @@ "message": "Items with no folder" }, "itemDetails": { - "message": "Item details" + "message": "項目詳細資訊" }, "itemName": { "message": "Item name" @@ -4297,7 +4288,7 @@ "message": "Are you sure you want to permanently delete this attachment?" }, "premium": { - "message": "Premium" + "message": "進階版" }, "freeOrgsCannotUseAttachments": { "message": "Free organizations cannot use attachments" @@ -4321,7 +4312,7 @@ } }, "personalDetails": { - "message": "Personal details" + "message": "個人資訊" }, "identification": { "message": "Identification" @@ -4411,10 +4402,10 @@ "message": "If you've renewed it, update the card's information" }, "cardDetails": { - "message": "Card details" + "message": "信用卡詳細資料" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "$BRAND$ 詳細資訊", "placeholders": { "brand": { "content": "$1", @@ -4438,7 +4429,7 @@ "message": "Data" }, "passkeys": { - "message": "Passkeys", + "message": "密碼金鑰", "description": "A section header for a list of passkeys." }, "passwords": { @@ -4446,7 +4437,7 @@ "description": "A section header for a list of passwords." }, "logInWithPasskeyAriaLabel": { - "message": "Log in with passkey", + "message": "使用密碼金鑰登入", "description": "ARIA label for the inline menu button that logs in with a passkey." }, "assign": { @@ -4897,5 +4888,14 @@ }, "beta": { "message": "Beta" + }, + "extensionWidth": { + "message": "Extension width" + }, + "wide": { + "message": "Wide" + }, + "extraWide": { + "message": "Extra wide" } } diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.html b/apps/browser/src/autofill/popup/settings/autofill.component.html index e9c9fd9c75..18c6f51533 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill.component.html @@ -160,7 +160,10 @@ {{ "enableAutoFillOnPageLoadDesc" | i18n }} - ' : '\'"> + {{ "warningCapitalized" | i18n }}: {{ "experimentalFeature" | i18n }} { }); describe("collectPageDetailsFromTab$", () => { - const tab = mock({ id: 1 }); + const tab = mock({ id: 1, url: "https://www.example.com" }); const messages = new Subject(); function mockCollectPageDetailsResponseMessage( @@ -165,11 +165,16 @@ describe("AutofillService", () => { it("sends a `collectPageDetails` message to the passed tab", () => { autofillService.collectPageDetailsFromTab$(tab); - expect(BrowserApi.tabSendMessage).toHaveBeenCalledWith(tab, { - command: AutofillMessageCommand.collectPageDetails, - sender: AutofillMessageSender.collectPageDetailsFromTabObservable, + expect(BrowserApi.tabSendMessage).toHaveBeenCalledWith( tab, - }); + { + command: AutofillMessageCommand.collectPageDetails, + sender: AutofillMessageSender.collectPageDetailsFromTabObservable, + tab, + }, + null, + true, + ); }); it("builds an array of page details from received `collectPageDetailsResponse` messages", async () => { @@ -218,6 +223,41 @@ describe("AutofillService", () => { expect(tracker.emissions[1]).toBeUndefined(); }); + + it("returns an empty array when the tab.url is empty", async () => { + const tracker = subscribeTo(autofillService.collectPageDetailsFromTab$({ ...tab, url: "" })); + + await tracker.pauseUntilReceived(1); + + expect(tracker.emissions[0]).toEqual([]); + }); + + it("returns an empty array when the `BrowserApi.tabSendMessage` throws an error", async () => { + (BrowserApi.tabSendMessage as jest.Mock).mockRejectedValueOnce(undefined); + + const tracker = subscribeTo(autofillService.collectPageDetailsFromTab$(tab)); + + await tracker.pauseUntilReceived(1); + + expect(tracker.emissions[0]).toEqual([]); + }); + + ["moz-extension://", "chrome-extension://", "safari-web-extension://"].forEach( + (extensionPrefix) => { + it(`returns an empty array when the tab.url starts with ${extensionPrefix}`, async () => { + const tracker = subscribeTo( + autofillService.collectPageDetailsFromTab$({ + ...tab, + url: `${extensionPrefix}/3e42342/popup/index.html`, + }), + ); + + await tracker.pauseUntilReceived(1); + + expect(tracker.emissions[0]).toEqual([]); + }); + }, + ); }); describe("loadAutofillScriptsOnInstall", () => { diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 24054794b6..eaaf23ca84 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -1,4 +1,4 @@ -import { filter, firstValueFrom, Observable, scan, startWith } from "rxjs"; +import { filter, firstValueFrom, merge, Observable, ReplaySubject, scan, startWith } from "rxjs"; import { pairwise } from "rxjs/operators"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; @@ -91,6 +91,9 @@ export default class AutofillService implements AutofillServiceInterface { * @param tab The tab to collect page details from */ collectPageDetailsFromTab$(tab: chrome.tabs.Tab): Observable { + /** Replay Subject that can be utilized when `messages$` may not emit the page details. */ + const pageDetailsFallback$ = new ReplaySubject<[]>(1); + const pageDetailsFromTab$ = this.messageListener .messages$(COLLECT_PAGE_DETAILS_RESPONSE_COMMAND) .pipe( @@ -112,13 +115,35 @@ export default class AutofillService implements AutofillServiceInterface { ), ); - void BrowserApi.tabSendMessage(tab, { - tab: tab, - command: AutofillMessageCommand.collectPageDetails, - sender: AutofillMessageSender.collectPageDetailsFromTabObservable, + void BrowserApi.tabSendMessage( + tab, + { + tab: tab, + command: AutofillMessageCommand.collectPageDetails, + sender: AutofillMessageSender.collectPageDetailsFromTabObservable, + }, + null, + true, + ).catch(() => { + // When `tabSendMessage` throws an error the `pageDetailsFromTab$` will not emit, + // fallback to an empty array + pageDetailsFallback$.next([]); }); - return pageDetailsFromTab$; + // Fallback to empty array when: + // - In Safari, `tabSendMessage` doesn't throw an error for this case. + // - When opening the extension directly via the URL, `tabSendMessage` doesn't always respond nor throw an error in FireFox. + // Adding checks for the major 3 browsers here to be safe. + const urlHasBrowserProtocol = [ + "moz-extension://", + "chrome-extension://", + "safari-web-extension://", + ].some((protocol) => tab.url.startsWith(protocol)); + if (!tab.url || urlHasBrowserProtocol) { + pageDetailsFallback$.next([]); + } + + return merge(pageDetailsFromTab$, pageDetailsFallback$); } /** diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 764304f4ff..b66f2457b2 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -725,7 +725,7 @@ export default class MainBackground { ); const sdkClientFactory = flagEnabled("sdk") - ? new BrowserSdkClientFactory() + ? new BrowserSdkClientFactory(this.logService) : new NoopSdkClientFactory(); this.sdkService = new DefaultSdkService( sdkClientFactory, diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index e81409e6e2..0ccc75cd5d 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": "2024.11.2", + "version": "2024.12.0", "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 1ece562622..32f58b0cc5 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": "2024.11.2", + "version": "2024.12.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 072ef74004..850e14302d 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -200,15 +200,17 @@ export class BrowserApi { tab: chrome.tabs.Tab, obj: T, options: chrome.tabs.MessageSendOptions = null, + rejectOnError = false, ): Promise { if (!tab || !tab.id) { return; } - return new Promise((resolve) => { + return new Promise((resolve, reject) => { chrome.tabs.sendMessage(tab.id, obj, options, (response) => { - if (chrome.runtime.lastError) { + if (chrome.runtime.lastError && rejectOnError) { // Some error happened + reject(); } resolve(response); }); diff --git a/apps/browser/src/platform/popup/browser-popup-utils.ts b/apps/browser/src/platform/popup/browser-popup-utils.ts index fb53d3451f..5459c0e220 100644 --- a/apps/browser/src/platform/popup/browser-popup-utils.ts +++ b/apps/browser/src/platform/popup/browser-popup-utils.ts @@ -1,6 +1,7 @@ import { BrowserApi } from "../browser/browser-api"; import { ScrollOptions } from "./abstractions/browser-popup-utils.abstractions"; +import { PopupWidthOptions } from "./layout/popup-width.service"; class BrowserPopupUtils { /** @@ -108,7 +109,10 @@ class BrowserPopupUtils { const defaultPopoutWindowOptions: chrome.windows.CreateData = { type: "popup", focused: true, - width: 380, + width: Math.max( + PopupWidthOptions.default, + typeof document === "undefined" ? PopupWidthOptions.default : document.body.clientWidth, + ), height: 630, }; const offsetRight = 15; diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index e80ac249ac..1aaea85e4a 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -514,3 +514,25 @@ export const TransparentHeader: Story = { `, }), }; + +export const WidthOptions: Story = { + render: (args) => ({ + props: args, + template: /* HTML */ ` + + Default: + + + + Wide: + + + + Extra wide: + + + + + `, + }), +}; diff --git a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html index 78b859f33b..a4ae3161b4 100644 --- a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html +++ b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html @@ -9,7 +9,7 @@ (POPUP_STYLE_DISK, "popup-width", { + deserializer: (s) => s, +}); + +/** + * Updates the extension popup width based on a user setting + **/ +@Injectable({ providedIn: "root" }) +export class PopupWidthService { + private static readonly LocalStorageKey = "bw-popup-width"; + private readonly state = inject(GlobalStateProvider).get(POPUP_WIDTH_KEY_DEF); + + readonly width$: Observable = this.state.state$.pipe( + map((state) => state ?? "default"), + ); + + async setWidth(width: PopupWidthOption) { + await this.state.update(() => width); + } + + /** Begin listening for state changes */ + init() { + this.width$.subscribe((width: PopupWidthOption) => { + PopupWidthService.setStyle(width); + localStorage.setItem(PopupWidthService.LocalStorageKey, width); + }); + } + + private static setStyle(width: PopupWidthOption) { + const pxWidth = PopupWidthOptions[width] ?? PopupWidthOptions.default; + document.body.style.minWidth = `${pxWidth}px`; + } + + /** + * To keep the popup size from flickering on bootstrap, we store the width in `localStorage` so we can quickly & synchronously reference it. + **/ + static initBodyWidthFromLocalStorage() { + const storedValue = localStorage.getItem(PopupWidthService.LocalStorageKey); + this.setStyle(storedValue as any); + } +} diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts index f9cfde73af..6ebbe3ff6b 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts @@ -1,4 +1,6 @@ +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; +import { RecoverableSDKError } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import type { BitwardenClient } from "@bitwarden/sdk-internal"; import { BrowserApi } from "../../browser/browser-api"; @@ -62,24 +64,39 @@ async function load() { * Works both in popup and service worker. */ export class BrowserSdkClientFactory implements SdkClientFactory { + constructor(private logService: LogService) {} + async createSdkClient( ...args: ConstructorParameters ): Promise { + const startTime = performance.now(); try { await loadWithTimeout(); } catch (error) { throw new Error(`Failed to load: ${error.message}`); } - return Promise.resolve((globalThis as any).init_sdk(...args)); + const endTime = performance.now(); + const elapsed = Math.round((endTime - startTime) / 1000); + + const instance = (globalThis as any).init_sdk(...args); + + this.logService.info("WASM SDK loaded in", Math.round(endTime - startTime), "ms"); + + // If it takes 3 seconds or more to load, we want to capture it. + if (elapsed >= 3) { + throw new RecoverableSDKError(instance, elapsed); + } + + return instance; } } const loadWithTimeout = async () => { return new Promise((resolve, reject) => { const timer = setTimeout(() => { - reject(new Error("Operation timed out after 1 second")); - }, 1000); + reject(new Error("Operation timed out after 10 second")); + }, 10000); load() .then(() => { diff --git a/apps/browser/src/popup/app-routing.animations.ts b/apps/browser/src/popup/app-routing.animations.ts index 061067c717..6af47934ef 100644 --- a/apps/browser/src/popup/app-routing.animations.ts +++ b/apps/browser/src/popup/app-routing.animations.ts @@ -1,4 +1,19 @@ -import { animate, group, query, style, transition, trigger } from "@angular/animations"; +import { + animate, + AnimationMetadata, + group, + query, + style, + transition, + trigger, +} from "@angular/animations"; + +/** + * Determines the router transition behavior. + * Changing between elevations will animate from the right. + * Navigating between two pages of the same elevation will not animate. + */ +export type RouteElevation = 0 | 1 | 2 | 3 | 4; const queryShown = query( ":enter, :leave", @@ -13,11 +28,15 @@ const queryChildRoute = query("router-outlet ~ *", [style({}), animate(1, style( optional: true, }); -const speed = "0.4s"; +const speedX = "0.225s"; +const speedY = "0.3s"; -export function queryTranslate( - direction: string, - axis: string, +type TranslateDirection = "enter" | "leave"; +type TranslationAxis = "X" | "Y"; + +function queryTranslate( + direction: TranslateDirection, + axis: TranslationAxis, from: number, to: number, zIndex = 1000, @@ -30,217 +49,67 @@ export function queryTranslate( zIndex: zIndex, boxShadow: "0 3px 2px -2px gray", }), - animate(speed + " ease-in-out", style({ transform: "translate" + axis + "(" + to + "%)" })), + animate( + (axis === "X" ? speedX : speedY) + " ease-in-out", + style({ + transform: "translate" + axis + "(" + to + "%)", + }), + ), ], - { optional: true }, + { + optional: true, + }, ); } -export function queryTranslateX(direction: string, from: number, to: number, zIndex = 1000) { - return queryTranslate(direction, "X", from, to, zIndex); -} - -export function queryTranslateY(direction: string, from: number, to: number, zIndex = 1000) { - return queryTranslate(direction, "Y", from, to, zIndex); -} - -const inSlideLeft = [ - queryShown, - group([queryTranslateX("enter", 100, 0), queryTranslateX("leave", 0, -100), queryChildRoute]), -]; - -const outSlideRight = [ - queryShown, - group([queryTranslateX("enter", -100, 0), queryTranslateX("leave", 0, 100)]), -]; - -const inSlideUp = [ - queryShown, - group([queryTranslateY("enter", 100, 0, 1010), queryTranslateY("leave", 0, 0), queryChildRoute]), -]; - -const outSlideDown = [ - queryShown, - group([queryTranslateY("enter", 0, 0), queryTranslateY("leave", 0, 100, 1010)]), -]; - -const inSlideDown = [ - queryShown, - group([queryTranslateY("enter", -100, 0, 1010), queryTranslateY("leave", 0, 0), queryChildRoute]), -]; - -// eslint-disable-next-line -const outSlideUp = [ - queryShown, - group([queryTranslateY("enter", 0, 0), queryTranslateY("leave", 0, -100, 1010)]), -]; - -export function tabsToCiphers(fromState: string, toState: string) { - if (fromState == null || toState === null || toState.indexOf("ciphers_") === -1) { - return false; - } - return ( - (fromState.indexOf("ciphers_") === 0 && fromState.indexOf("ciphers_direction=b") === -1) || - fromState === "tabs" - ); -} - -export function ciphersToTabs(fromState: string, toState: string) { - if (fromState == null || toState === null || fromState.indexOf("ciphers_") === -1) { - return false; - } - return toState.indexOf("ciphers_direction=b") === 0 || toState === "tabs"; -} - -export function ciphersToView(fromState: string, toState: string) { - if (fromState == null || toState === null) { - return false; - } - return ( - fromState.indexOf("ciphers_") === 0 && - (toState === "view-cipher" || toState === "add-cipher" || toState === "clone-cipher") - ); -} - -export function viewToCiphers(fromState: string, toState: string) { - if (fromState == null || toState === null) { - return false; - } - return ( - (fromState === "view-cipher" || fromState === "add-cipher" || fromState === "clone-cipher") && - toState.indexOf("ciphers_") === 0 - ); -} +const animations = { + slideInFromRight: [ + queryShown, + group([ + queryTranslate("enter", "X", 100, 0, 1010), + queryTranslate("leave", "X", 0, 0), + queryChildRoute, + ]), + ], + slideOutToRight: [ + queryShown, + group([queryTranslate("enter", "X", 0, 0), queryTranslate("leave", "X", 0, 100, 1010)]), + ], + /** --- Not used --- */ + // slideInFromTop: [ + // queryShown, + // group([ + // queryTranslate("enter", "Y", -100, 0, 1010), + // queryTranslate("leave", "Y", 0, 0), + // queryChildRoute, + // ]), + // ], + // slideOutToTop: [ + // queryShown, + // group([queryTranslate("enter", "Y", 0, 0), queryTranslate("leave", "Y", 0, -100, 1010)]), + // ], +} satisfies Record; export const routerTransition = trigger("routerTransition", [ - transition("void => home", inSlideLeft), - transition("void => tabs", inSlideLeft), + transition("0 => 1", animations.slideInFromRight), + transition("0 => 2", animations.slideInFromRight), + transition("0 => 3", animations.slideInFromRight), + transition("0 => 4", animations.slideInFromRight), + transition("1 => 2", animations.slideInFromRight), + transition("1 => 3", animations.slideInFromRight), + transition("1 => 4", animations.slideInFromRight), + transition("2 => 3", animations.slideInFromRight), + transition("2 => 4", animations.slideInFromRight), + transition("3 => 4", animations.slideInFromRight), - transition("home => environment, home => login, home => register", inSlideUp), - - transition("login => home", outSlideDown), - transition("login => hint", inSlideUp), - transition("login => tabs, login => 2fa, login => login-with-device", inSlideLeft), - - transition("hint => login, register => home, environment => home", outSlideDown), - - transition("2fa => login", outSlideRight), - transition("2fa => 2fa-options", inSlideUp), - transition("2fa-options => 2fa", outSlideDown), - transition("2fa => tabs", inSlideLeft), - - transition("login-with-device => tabs, login-with-device => 2fa", inSlideLeft), - transition("login-with-device => login", outSlideRight), - - transition("admin-approval-requested => tabs, admin-approval-requested => 2fa", inSlideLeft), - transition("admin-approval-requested => login", outSlideRight), - - transition(tabsToCiphers, inSlideLeft), - transition(ciphersToTabs, outSlideRight), - - transition(ciphersToView, inSlideUp), - transition(viewToCiphers, outSlideDown), - - transition("tabs => view-cipher", inSlideUp), - transition("view-cipher => tabs", outSlideDown), - - transition("view-cipher => edit-cipher, view-cipher => cipher-password-history", inSlideUp), - transition( - "edit-cipher => view-cipher, cipher-password-history => view-cipher, edit-cipher => tabs", - outSlideDown, - ), - - transition("view-cipher => clone-cipher", inSlideUp), - transition("clone-cipher => view-cipher, clone-cipher => tabs", outSlideDown), - - transition("view-cipher => share-cipher", inSlideUp), - transition("share-cipher => view-cipher", outSlideDown), - - transition("tabs => add-cipher", inSlideUp), - transition("add-cipher => tabs", outSlideDown), - - transition("generator => generator-history, tabs => generator-history", inSlideLeft), - transition("generator-history => generator, generator-history => tabs", outSlideRight), - - transition( - "add-cipher => generator, edit-cipher => generator, clone-cipher => generator", - inSlideUp, - ), - transition( - "generator => add-cipher, generator => edit-cipher, generator => clone-cipher", - outSlideDown, - ), - - transition("edit-cipher => attachments, edit-cipher => collections", inSlideLeft), - transition("attachments => edit-cipher, collections => edit-cipher", outSlideRight), - - transition("clone-cipher => attachments, clone-cipher => collections", inSlideLeft), - transition("attachments => clone-cipher, collections => clone-cipher", outSlideRight), - - transition("tabs => account-security", inSlideLeft), - transition("account-security => tabs", outSlideRight), - - transition("tabs => assign-collections", inSlideLeft), - transition("assign-collections => tabs", outSlideRight), - - // Vault settings - transition("tabs => vault-settings", inSlideLeft), - transition("vault-settings => tabs", outSlideRight), - - transition("vault-settings => import", inSlideLeft), - transition("import => vault-settings", outSlideRight), - - transition("vault-settings => export", inSlideLeft), - transition("export => vault-settings", outSlideRight), - - transition("vault-settings => folders", inSlideLeft), - transition("folders => vault-settings", outSlideRight), - - transition("folders => edit-folder, folders => add-folder", inSlideUp), - transition("edit-folder => folders, add-folder => folders", outSlideDown), - - transition("vault-settings => sync", inSlideLeft), - transition("sync => vault-settings", outSlideRight), - - transition("vault-settings => trash", inSlideLeft), - transition("trash => vault-settings", outSlideRight), - - transition("trash => view-cipher", inSlideLeft), - transition("view-cipher => trash", outSlideRight), - - // Appearance settings - transition("tabs => appearance", inSlideLeft), - transition("appearance => tabs", outSlideRight), - - transition("tabs => premium", inSlideLeft), - transition("premium => tabs", outSlideRight), - - transition("tabs => lock", inSlideDown), - - transition("tabs => about", inSlideLeft), - transition("about => tabs", outSlideRight), - - transition("tabs => send-type", inSlideLeft), - transition("send-type => tabs", outSlideRight), - - transition("tabs => add-send, send-type => add-send", inSlideUp), - transition("add-send => tabs, add-send => send-type", outSlideDown), - - transition("tabs => edit-send, send-type => edit-send", inSlideUp), - transition("edit-send => tabs, edit-send => send-type", outSlideDown), - - // Notification settings - transition("tabs => notifications", inSlideLeft), - transition("notifications => tabs", outSlideRight), - - transition("notifications => excluded-domains", inSlideLeft), - transition("excluded-domains => notifications", outSlideRight), - - transition("tabs => autofill", inSlideLeft), - transition("autofill => tabs", outSlideRight), - - transition("* => account-switcher", inSlideUp), - transition("account-switcher => *", outSlideDown), - - transition("lock => *", outSlideDown), + transition("1 => 0", animations.slideOutToRight), + transition("2 => 0", animations.slideOutToRight), + transition("2 => 1", animations.slideOutToRight), + transition("3 => 0", animations.slideOutToRight), + transition("3 => 1", animations.slideOutToRight), + transition("3 => 2", animations.slideOutToRight), + transition("4 => 0", animations.slideOutToRight), + transition("4 => 1", animations.slideOutToRight), + transition("4 => 2", animations.slideOutToRight), + transition("4 => 3", animations.slideOutToRight), ]); diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 3ce58dd746..fc07078945 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -6,6 +6,7 @@ import { EnvironmentSelectorRouteData, ExtensionDefaultOverlayPosition, } from "@bitwarden/angular/auth/components/environment-selector.component"; +import { TwoFactorTimeoutComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-expired.component"; import { unauthUiRefreshRedirect } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-redirect"; import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap"; import { @@ -38,6 +39,7 @@ import { VaultIcon, LoginDecryptionOptionsComponent, DevicesIcon, + TwoFactorTimeoutIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -122,6 +124,7 @@ import { TrashComponent } from "../vault/popup/settings/trash.component"; import { VaultSettingsV2Component } from "../vault/popup/settings/vault-settings-v2.component"; import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.component"; +import { RouteElevation } from "./app-routing.animations"; import { debounceNavigationGuard } from "./services/debounce-navigation.service"; import { TabsV2Component } from "./tabs-v2.component"; import { TabsComponent } from "./tabs.component"; @@ -130,13 +133,10 @@ import { TabsComponent } from "./tabs.component"; * Data properties acceptable for use in extension route objects */ export interface RouteDataProperties { + elevation: RouteElevation; + /** - * A state string which identifies the current route for the sake of transition animation logic. - * The state string is passed into [@routerTransition] in the app.component. - */ - state: string; - /** - * A boolean to indicate that the URL should not be saved in memory in the BrowserRouterSvc. + * A boolean to indicate that the URL should not be saved in memory in the BrowserRouterService. */ doNotSaveUrl?: boolean; } @@ -166,19 +166,19 @@ const routes: Routes = [ path: "home", component: HomeComponent, canActivate: [unauthGuardFn(unauthRouteOverrides), unauthUiRefreshRedirect("/login")], - data: { state: "home" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(Fido2V1Component, Fido2Component, { path: "fido2", canActivate: [fido2AuthGuard], - data: { state: "fido2" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), { path: "lock", component: LockComponent, canActivate: [lockGuard()], canMatch: [extensionRefreshRedirect("/lockV2")], - data: { state: "lock", doNotSaveUrl: true } satisfies RouteDataProperties, + data: { elevation: 1, doNotSaveUrl: true } satisfies RouteDataProperties, }, ...unauthUiRefreshSwap( TwoFactorComponentV1, @@ -186,12 +186,12 @@ const routes: Routes = [ { path: "2fa", canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "2fa" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "2fa", canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "2fa" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, children: [ { path: "", @@ -200,205 +200,234 @@ const routes: Routes = [ ], }, ), + { + path: "", + component: ExtensionAnonLayoutWrapperComponent, + children: [ + { + path: "2fa-timeout", + canActivate: [unauthGuardFn(unauthRouteOverrides)], + children: [ + { + path: "", + component: TwoFactorTimeoutComponent, + }, + ], + data: { + pageTitle: { + key: "authenticationTimeout", + }, + pageIcon: TwoFactorTimeoutIcon, + elevation: 1, + } satisfies RouteDataProperties & AnonLayoutWrapperData, + }, + ], + }, { path: "2fa-options", component: TwoFactorOptionsComponent, canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "2fa-options" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "sso", component: SsoComponent, canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "sso" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "set-password", component: SetPasswordComponent, - data: { state: "set-password" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "remove-password", component: RemovePasswordComponent, canActivate: [authGuard], - data: { state: "remove-password" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "register", component: RegisterComponent, canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "register" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "environment", component: EnvironmentComponent, canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "environment" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "ciphers", component: VaultItemsComponent, canActivate: [authGuard], - data: { state: "ciphers" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(ViewComponent, ViewV2Component, { path: "view-cipher", canActivate: [authGuard], - data: { state: "view-cipher" } satisfies RouteDataProperties, + data: { + // Above "trash" + elevation: 3, + } satisfies RouteDataProperties, }), ...extensionRefreshSwap(PasswordHistoryComponent, PasswordHistoryV2Component, { path: "cipher-password-history", canActivate: [authGuard], - data: { state: "cipher-password-history" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(AddEditComponent, AddEditV2Component, { path: "add-cipher", canActivate: [authGuard, debounceNavigationGuard()], - data: { state: "add-cipher" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, runGuardsAndResolvers: "always", }), ...extensionRefreshSwap(AddEditComponent, AddEditV2Component, { path: "edit-cipher", canActivate: [authGuard, debounceNavigationGuard()], - data: { state: "edit-cipher" } satisfies RouteDataProperties, + data: { + // Above "trash" + elevation: 3, + } satisfies RouteDataProperties, runGuardsAndResolvers: "always", }), { path: "share-cipher", component: ShareComponent, canActivate: [authGuard], - data: { state: "share-cipher" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "collections", component: CollectionsComponent, canActivate: [authGuard], - data: { state: "collections" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(AttachmentsComponent, AttachmentsV2Component, { path: "attachments", canActivate: [authGuard], - data: { state: "attachments" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), { path: "generator", component: GeneratorComponent, canActivate: [authGuard], - data: { state: "generator" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(PasswordGeneratorHistoryComponent, CredentialGeneratorHistoryComponent, { path: "generator-history", canActivate: [authGuard], - data: { state: "generator-history" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(ImportBrowserComponent, ImportBrowserV2Component, { path: "import", canActivate: [authGuard], - data: { state: "import" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(ExportBrowserComponent, ExportBrowserV2Component, { path: "export", canActivate: [authGuard], - data: { state: "export" } satisfies RouteDataProperties, + data: { elevation: 2 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(AutofillV1Component, AutofillComponent, { path: "autofill", canActivate: [authGuard], - data: { state: "autofill" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(AccountSecurityV1Component, AccountSecurityComponent, { path: "account-security", canActivate: [authGuard], - data: { state: "account-security" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(NotificationsSettingsV1Component, NotificationsSettingsComponent, { path: "notifications", canActivate: [authGuard], - data: { state: "notifications" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(VaultSettingsComponent, VaultSettingsV2Component, { path: "vault-settings", canActivate: [authGuard], - data: { state: "vault-settings" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(FoldersComponent, FoldersV2Component, { path: "folders", canActivate: [authGuard], - data: { state: "folders" } satisfies RouteDataProperties, + data: { elevation: 2 } satisfies RouteDataProperties, }), { path: "add-folder", component: FolderAddEditComponent, canActivate: [authGuard], - data: { state: "add-folder" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "edit-folder", component: FolderAddEditComponent, canActivate: [authGuard], - data: { state: "edit-folder" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "sync", component: SyncComponent, canActivate: [authGuard], - data: { state: "sync" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(ExcludedDomainsV1Component, ExcludedDomainsComponent, { path: "excluded-domains", canActivate: [authGuard], - data: { state: "excluded-domains" } satisfies RouteDataProperties, + data: { elevation: 2 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(PremiumComponent, PremiumV2Component, { path: "premium", component: PremiumComponent, canActivate: [authGuard], - data: { state: "premium" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(AppearanceComponent, AppearanceV2Component, { path: "appearance", canActivate: [authGuard], - data: { state: "appearance" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(AddEditComponent, AddEditV2Component, { path: "clone-cipher", canActivate: [authGuard], - data: { state: "clone-cipher" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), { path: "send-type", component: SendTypeComponent, canActivate: [authGuard], - data: { state: "send-type" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, { path: "add-send", canActivate: [authGuard], - data: { state: "add-send" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, { path: "edit-send", canActivate: [authGuard], - data: { state: "edit-send" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), { path: "send-created", component: SendCreatedComponent, canActivate: [authGuard], - data: { state: "send" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "update-temp-password", component: UpdateTempPasswordComponent, canActivate: [authGuard], - data: { state: "update-temp-password" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...unauthUiRefreshSwap( LoginViaAuthRequestComponentV1, ExtensionAnonLayoutWrapperComponent, { path: "login-with-device", - data: { state: "login-with-device" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "login-with-device", @@ -412,7 +441,7 @@ const routes: Routes = [ }, showLogo: false, showBackButton: true, - state: "login-with-device", + elevation: 1, } satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData, children: [ { path: "", component: LoginViaAuthRequestComponent }, @@ -429,7 +458,7 @@ const routes: Routes = [ ExtensionAnonLayoutWrapperComponent, { path: "admin-approval-requested", - data: { state: "admin-approval-requested" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "admin-approval-requested", @@ -443,7 +472,7 @@ const routes: Routes = [ }, showLogo: false, showBackButton: true, - state: "admin-approval-requested", + elevation: 1, } satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData, children: [{ path: "", component: LoginViaAuthRequestComponent }], }, @@ -455,7 +484,7 @@ const routes: Routes = [ path: "hint", canActivate: [unauthGuardFn(unauthRouteOverrides)], data: { - state: "hint", + elevation: 1, } satisfies RouteDataProperties, }, { @@ -473,7 +502,7 @@ const routes: Routes = [ }, pageIcon: UserLockIcon, showBackButton: true, - state: "hint", + elevation: 1, } satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData, children: [ { path: "", component: PasswordHintComponent }, @@ -496,7 +525,7 @@ const routes: Routes = [ { path: "login", canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "login" }, + data: { elevation: 1 }, }, { path: "", @@ -509,7 +538,7 @@ const routes: Routes = [ pageTitle: { key: "logInToBitwarden", }, - state: "login", + elevation: 1, showAcctSwitcher: true, } satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData, children: [ @@ -534,7 +563,7 @@ const routes: Routes = [ { path: "login-initiated", canActivate: [tdeDecryptionRequiredGuard()], - data: { state: "login-initiated" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "login-initiated", @@ -553,7 +582,7 @@ const routes: Routes = [ path: "signup", canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], data: { - state: "signup", + elevation: 1, pageIcon: RegistrationUserAddIcon, pageTitle: { key: "createAccount", @@ -580,7 +609,7 @@ const routes: Routes = [ canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], data: { pageIcon: RegistrationLockAltIcon, - state: "finish-signup", + elevation: 1, showBackButton: true, } satisfies RouteDataProperties & ExtensionAnonLayoutWrapperData, children: [ @@ -625,7 +654,7 @@ const routes: Routes = [ pageSubtitle: { key: "finishJoiningThisOrganizationBySettingAMasterPassword", }, - state: "set-password-jit", + elevation: 1, } satisfies RouteDataProperties & AnonLayoutWrapperData, }, ], @@ -634,21 +663,21 @@ const routes: Routes = [ path: "assign-collections", component: AssignCollections, canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh, true, "/")], - data: { state: "assign-collections" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }, ...extensionRefreshSwap(AboutPageComponent, AboutPageV2Component, { path: "about", canActivate: [authGuard], - data: { state: "about" } satisfies RouteDataProperties, + data: { elevation: 1 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(MoreFromBitwardenPageComponent, MoreFromBitwardenPageV2Component, { path: "more-from-bitwarden", canActivate: [authGuard], - data: { state: "moreFromBitwarden" } satisfies RouteDataProperties, + data: { elevation: 2 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(TabsComponent, TabsV2Component, { path: "tabs", - data: { state: "tabs" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, children: [ { path: "", @@ -660,42 +689,42 @@ const routes: Routes = [ component: CurrentTabComponent, canActivate: [authGuard], canMatch: [extensionRefreshRedirect("/tabs/vault")], - data: { state: "tabs_current" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, runGuardsAndResolvers: "always", }, ...extensionRefreshSwap(VaultFilterComponent, VaultV2Component, { path: "vault", canActivate: [authGuard], canDeactivate: [clearVaultStateGuard], - data: { state: "tabs_vault" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(GeneratorComponent, CredentialGeneratorComponent, { path: "generator", canActivate: [authGuard], - data: { state: "tabs_generator" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(SettingsComponent, SettingsV2Component, { path: "settings", canActivate: [authGuard], - data: { state: "tabs_settings" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, }), ...extensionRefreshSwap(SendGroupingsComponent, SendV2Component, { path: "send", canActivate: [authGuard], - data: { state: "tabs_send" } satisfies RouteDataProperties, + data: { elevation: 0 } satisfies RouteDataProperties, }), ], }), { path: "account-switcher", component: AccountSwitcherComponent, - data: { state: "account-switcher", doNotSaveUrl: true } satisfies RouteDataProperties, + data: { elevation: 4, doNotSaveUrl: true } satisfies RouteDataProperties, }, { path: "trash", component: TrashComponent, canActivate: [authGuard], - data: { state: "trash" } satisfies RouteDataProperties, + data: { elevation: 2 } satisfies RouteDataProperties, }, ]; diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index c23da5ca7c..15dfcabe5f 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -25,6 +25,7 @@ import { import { flagEnabled } from "../platform/flags"; import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service"; +import { PopupWidthService } from "../platform/popup/layout/popup-width.service"; import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service"; import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service"; import { BrowserSendStateService } from "../tools/popup/services/browser-send-state.service"; @@ -37,13 +38,14 @@ import { DesktopSyncVerificationDialogComponent } from "./components/desktop-syn selector: "app-root", styles: [], animations: [routerTransition], - template: ` - + template: ` + `, }) export class AppComponent implements OnInit, OnDestroy { private viewCacheService = inject(PopupViewCacheService); private compactModeService = inject(PopupCompactModeService); + private widthService = inject(PopupWidthService); private lastActivity: Date; private activeUserId: UserId; @@ -99,6 +101,7 @@ export class AppComponent implements OnInit, OnDestroy { await this.viewCacheService.init(); this.compactModeService.init(); + this.widthService.init(); // Component states must not persist between closing and reopening the popup, otherwise they become dead objects // Clear them aggressively to make sure this doesn't occur @@ -220,23 +223,12 @@ export class AppComponent implements OnInit, OnDestroy { this.destroy$.complete(); } - getState(outlet: RouterOutlet) { + getRouteElevation(outlet: RouterOutlet) { if (!this.routerAnimations) { return; - } else if (outlet.activatedRouteData.state === "ciphers") { - const routeDirection = - (window as any).routeDirection != null ? (window as any).routeDirection : ""; - return ( - "ciphers_direction=" + - routeDirection + - "_" + - (outlet.activatedRoute.queryParams as any).value.folderId + - "_" + - (outlet.activatedRoute.queryParams as any).value.collectionId - ); - } else { - return outlet.activatedRouteData.state; } + + return outlet.activatedRouteData.elevation; } private async recordActivity() { diff --git a/apps/browser/src/popup/main.ts b/apps/browser/src/popup/main.ts index c98e30e6bc..db634ea2e2 100644 --- a/apps/browser/src/popup/main.ts +++ b/apps/browser/src/popup/main.ts @@ -1,6 +1,7 @@ import { enableProdMode } from "@angular/core"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; +import { PopupWidthService } from "../platform/popup/layout/popup-width.service"; import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service"; require("./scss/popup.scss"); @@ -8,7 +9,8 @@ require("./scss/tailwind.css"); import { AppModule } from "./app.module"; -// We put this first to minimize the delay in window changing. +// We put these first to minimize the delay in window changing. +PopupWidthService.initBodyWidthFromLocalStorage(); // Should be removed once we deprecate support for Safari 16.0 and older. See Jira ticket [PM-1861] if (BrowserPlatformUtilsService.shouldApplySafariHeightFix(window)) { document.documentElement.classList.add("safari_height_fix"); diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss index 89b8816567..49847dd777 100644 --- a/apps/browser/src/popup/scss/base.scss +++ b/apps/browser/src/popup/scss/base.scss @@ -19,7 +19,7 @@ body { } body { - width: 380px !important; + min-width: 380px; height: 600px !important; position: relative; min-height: 100vh; diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index b68102033b..18d109776a 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -576,8 +576,9 @@ const safeProviders: SafeProvider[] = [ }), safeProvider({ provide: SdkClientFactory, - useClass: flagEnabled("sdk") ? BrowserSdkClientFactory : NoopSdkClientFactory, - deps: [], + useFactory: (logService) => + flagEnabled("sdk") ? new BrowserSdkClientFactory(logService) : new NoopSdkClientFactory(), + deps: [LogService], }), safeProvider({ provide: LoginEmailService, diff --git a/apps/browser/src/services/families-policy.service.spec.ts b/apps/browser/src/services/families-policy.service.spec.ts new file mode 100644 index 0000000000..19291bcd82 --- /dev/null +++ b/apps/browser/src/services/families-policy.service.spec.ts @@ -0,0 +1,83 @@ +import { TestBed } from "@angular/core/testing"; +import { mock, MockProxy } from "jest-mock-extended"; +import { firstValueFrom, of } from "rxjs"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; + +import { FamiliesPolicyService } from "./families-policy.service"; // Adjust the import as necessary + +describe("FamiliesPolicyService", () => { + let service: FamiliesPolicyService; + let organizationService: MockProxy; + let policyService: MockProxy; + + beforeEach(() => { + organizationService = mock(); + policyService = mock(); + + TestBed.configureTestingModule({ + providers: [ + FamiliesPolicyService, + { provide: OrganizationService, useValue: organizationService }, + { provide: PolicyService, useValue: policyService }, + ], + }); + + service = TestBed.inject(FamiliesPolicyService); + }); + + it("should return false when there are no enterprise organizations", async () => { + jest.spyOn(service, "hasSingleEnterpriseOrg$").mockReturnValue(of(false)); + + const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$()); + expect(result).toBe(false); + }); + + it("should return true when the policy is enabled for the one enterprise organization", async () => { + jest.spyOn(service, "hasSingleEnterpriseOrg$").mockReturnValue(of(true)); + + const organizations = [{ id: "org1", canManageSponsorships: true }] as Organization[]; + organizationService.getAll$.mockReturnValue(of(organizations)); + + const policies = [{ organizationId: "org1", enabled: true }] as Policy[]; + policyService.getAll$.mockReturnValue(of(policies)); + + const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$()); + expect(result).toBe(true); + }); + + it("should return false when the policy is not enabled for the one enterprise organization", async () => { + jest.spyOn(service, "hasSingleEnterpriseOrg$").mockReturnValue(of(true)); + + const organizations = [{ id: "org1", canManageSponsorships: true }] as Organization[]; + organizationService.getAll$.mockReturnValue(of(organizations)); + + const policies = [{ organizationId: "org1", enabled: false }] as Policy[]; + policyService.getAll$.mockReturnValue(of(policies)); + + const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$()); + expect(result).toBe(false); + }); + + it("should return true when there is exactly one enterprise organization that can manage sponsorships", async () => { + const organizations = [{ id: "org1", canManageSponsorships: true }] as Organization[]; + organizationService.getAll$.mockReturnValue(of(organizations)); + + const result = await firstValueFrom(service.hasSingleEnterpriseOrg$()); + expect(result).toBe(true); + }); + + it("should return false when there are multiple organizations that can manage sponsorships", async () => { + const organizations = [ + { id: "org1", canManageSponsorships: true }, + { id: "org2", canManageSponsorships: true }, + ] as Organization[]; + organizationService.getAll$.mockReturnValue(of(organizations)); + + const result = await firstValueFrom(service.hasSingleEnterpriseOrg$()); + expect(result).toBe(false); + }); +}); diff --git a/apps/browser/src/services/families-policy.service.ts b/apps/browser/src/services/families-policy.service.ts new file mode 100644 index 0000000000..426f39dcfd --- /dev/null +++ b/apps/browser/src/services/families-policy.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from "@angular/core"; +import { map, Observable, of, switchMap } from "rxjs"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; + +@Injectable({ providedIn: "root" }) +export class FamiliesPolicyService { + constructor( + private policyService: PolicyService, + private organizationService: OrganizationService, + ) {} + + hasSingleEnterpriseOrg$(): Observable { + // Retrieve all organizations the user is part of + return this.organizationService.getAll$().pipe( + map((organizations) => { + // Filter to only those organizations that can manage sponsorships + const sponsorshipOrgs = organizations.filter((org) => org.canManageSponsorships); + + // Check if there is exactly one organization that can manage sponsorships. + // This is important because users that are part of multiple organizations + // may always access free bitwarden family menu. We want to restrict access + // to the policy only when there is a single enterprise organization and the free family policy is turn. + return sponsorshipOrgs.length === 1; + }), + ); + } + + isFreeFamilyPolicyEnabled$(): Observable { + return this.hasSingleEnterpriseOrg$().pipe( + switchMap((hasSingleEnterpriseOrg) => { + if (!hasSingleEnterpriseOrg) { + return of(false); + } + return this.organizationService.getAll$().pipe( + map((organizations) => organizations.find((org) => org.canManageSponsorships)?.id), + switchMap((enterpriseOrgId) => + this.policyService + .getAll$(PolicyType.FreeFamiliesSponsorshipPolicy) + .pipe( + map( + (policies) => + policies.find((policy) => policy.organizationId === enterpriseOrgId)?.enabled ?? + false, + ), + ), + ), + ); + }), + ); + } +} diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html index 9322ab5113..a2d01ce752 100644 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html +++ b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.html @@ -12,7 +12,12 @@ - + {{ "freeBitwardenFamilies" | i18n }} diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts index 7cdb691d56..2d451dddaa 100644 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page-v2.component.ts @@ -13,6 +13,7 @@ import { BrowserApi } from "../../../../platform/browser/browser-api"; import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; import { PopupHeaderComponent } from "../../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page.component"; +import { FamiliesPolicyService } from "../../../../services/families-policy.service"; @Component({ templateUrl: "more-from-bitwarden-page-v2.component.html", @@ -30,15 +31,20 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page export class MoreFromBitwardenPageV2Component { canAccessPremium$: Observable; protected familySponsorshipAvailable$: Observable; + protected isFreeFamilyPolicyEnabled$: Observable; + protected hasSingleEnterpriseOrg$: Observable; constructor( private dialogService: DialogService, billingAccountProfileStateService: BillingAccountProfileStateService, private environmentService: EnvironmentService, private organizationService: OrganizationService, + private familiesPolicyService: FamiliesPolicyService, ) { this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; this.familySponsorshipAvailable$ = this.organizationService.familySponsorshipAvailable$; + this.hasSingleEnterpriseOrg$ = this.familiesPolicyService.hasSingleEnterpriseOrg$(); + this.isFreeFamilyPolicyEnabled$ = this.familiesPolicyService.isFreeFamilyPolicyEnabled$(); } async openFreeBitwardenFamiliesPage() { diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html index bf9decca4e..8e7b349536 100644 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html +++ b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html @@ -30,6 +30,7 @@ class="box-content-row box-content-row-flex text-default" appStopClick (click)="openFreeBitwardenFamiliesPage()" + *ngIf="!((isFreeFamilyPolicyEnabled$ | async) && (hasSingleEnterpriseOrg$ | async))" > {{ "freeBitwardenFamilies" | i18n }} diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts index a9e9e797bf..1f26d40b34 100644 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts +++ b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts @@ -10,6 +10,7 @@ import { DialogService } from "@bitwarden/components"; import { BrowserApi } from "../../../../platform/browser/browser-api"; import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; +import { FamiliesPolicyService } from "../../../../services/families-policy.service"; @Component({ templateUrl: "more-from-bitwarden-page.component.html", @@ -18,13 +19,18 @@ import { PopOutComponent } from "../../../../platform/popup/components/pop-out.c }) export class MoreFromBitwardenPageComponent { canAccessPremium$: Observable; + protected isFreeFamilyPolicyEnabled$: Observable; + protected hasSingleEnterpriseOrg$: Observable; constructor( private dialogService: DialogService, private billingAccountProfileStateService: BillingAccountProfileStateService, private environmentService: EnvironmentService, + private familiesPolicyService: FamiliesPolicyService, ) { this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; + this.hasSingleEnterpriseOrg$ = this.familiesPolicyService.hasSingleEnterpriseOrg$(); + this.isFreeFamilyPolicyEnabled$ = this.familiesPolicyService.isFreeFamilyPolicyEnabled$(); } async openFreeBitwardenFamiliesPage() { diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html index 78403784f4..7b31e647bf 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html @@ -3,19 +3,27 @@ {{ "new" | i18n }} - + {{ "typeLogin" | i18n }} - + {{ "typeCard" | i18n }} - + {{ "typeIdentity" | i18n }} - + {{ "note" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts index 6842f35ea6..b5dc2a2f03 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.spec.ts @@ -1,141 +1,163 @@ import { CommonModule } from "@angular/common"; import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { Router } from "@angular/router"; +import { ActivatedRoute, RouterLink } from "@angular/router"; +import { mock } from "jest-mock-extended"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; -import { ButtonModule, DialogService, MenuModule } from "@bitwarden/components"; +import { ButtonModule, DialogService, MenuModule, NoItemsModule } from "@bitwarden/components"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; -import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; -import { AddEditFolderDialogComponent } from "../add-edit-folder-dialog/add-edit-folder-dialog.component"; import { NewItemDropdownV2Component, NewItemInitialValues } from "./new-item-dropdown-v2.component"; describe("NewItemDropdownV2Component", () => { let component: NewItemDropdownV2Component; let fixture: ComponentFixture; - const open = jest.fn(); - const navigate = jest.fn(); + let dialogServiceMock: jest.Mocked; + let browserApiMock: jest.Mocked; - jest - .spyOn(BrowserApi, "getTabFromCurrentWindow") - .mockResolvedValue({ url: "https://example.com" } as chrome.tabs.Tab); + const mockTab = { url: "https://example.com" }; + + beforeAll(() => { + jest.spyOn(BrowserApi, "getTabFromCurrentWindow").mockResolvedValue(mockTab as chrome.tabs.Tab); + jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValue(false); + jest.spyOn(Utils, "getHostname").mockReturnValue("example.com"); + }); beforeEach(async () => { - open.mockClear(); - navigate.mockClear(); + dialogServiceMock = mock(); + dialogServiceMock.open.mockClear(); + + const activatedRouteMock = { + snapshot: { paramMap: { get: jest.fn() } }, + }; + + const i18nServiceMock = mock(); + const folderServiceMock = mock(); + const folderApiServiceAbstractionMock = mock(); + const accountServiceMock = mock(); await TestBed.configureTestingModule({ - imports: [NewItemDropdownV2Component, MenuModule, ButtonModule, JslibModule, CommonModule], - providers: [ - { provide: I18nService, useValue: { t: (key: string) => key } }, - { provide: Router, useValue: { navigate } }, + imports: [ + CommonModule, + RouterLink, + ButtonModule, + MenuModule, + NoItemsModule, + NewItemDropdownV2Component, ], - }) - .overrideProvider(DialogService, { useValue: { open } }) - .compileComponents(); + providers: [ + { provide: DialogService, useValue: dialogServiceMock }, + { provide: I18nService, useValue: i18nServiceMock }, + { provide: ActivatedRoute, useValue: activatedRouteMock }, + { provide: BrowserApi, useValue: browserApiMock }, + { provide: FolderService, useValue: folderServiceMock }, + { provide: FolderApiServiceAbstraction, useValue: folderApiServiceAbstractionMock }, + { provide: AccountService, useValue: accountServiceMock }, + ], + }).compileComponents(); + }); + beforeEach(() => { fixture = TestBed.createComponent(NewItemDropdownV2Component); component = fixture.componentInstance; fixture.detectChanges(); }); - it("opens new folder dialog", () => { - component.openFolderDialog(); - - expect(open).toHaveBeenCalledWith(AddEditFolderDialogComponent); - }); - - describe("new item", () => { - const emptyParams: AddEditQueryParams = { - collectionId: undefined, - organizationId: undefined, - folderId: undefined, - }; - - beforeEach(() => { - jest.spyOn(component, "newItemNavigate"); - }); - - it("navigates to new login", async () => { - await component.newItemNavigate(CipherType.Login); - - expect(navigate).toHaveBeenCalledWith(["/add-cipher"], { - queryParams: { - type: CipherType.Login.toString(), - name: "example.com", - uri: "https://example.com", - ...emptyParams, - }, - }); - }); - - it("navigates to new card", async () => { - await component.newItemNavigate(CipherType.Card); - - expect(navigate).toHaveBeenCalledWith(["/add-cipher"], { - queryParams: { type: CipherType.Card.toString(), ...emptyParams }, - }); - }); - - it("navigates to new identity", async () => { - await component.newItemNavigate(CipherType.Identity); - - expect(navigate).toHaveBeenCalledWith(["/add-cipher"], { - queryParams: { type: CipherType.Identity.toString(), ...emptyParams }, - }); - }); - - it("navigates to new note", async () => { - await component.newItemNavigate(CipherType.SecureNote); - - expect(navigate).toHaveBeenCalledWith(["/add-cipher"], { - queryParams: { type: CipherType.SecureNote.toString(), ...emptyParams }, - }); - }); - - it("includes initial values", async () => { + describe("buildQueryParams", () => { + it("should build query params for a Login cipher when not popped out", async () => { + await component.ngOnInit(); component.initialValues = { folderId: "222-333-444", organizationId: "444-555-666", collectionId: "777-888-999", } as NewItemInitialValues; - await component.newItemNavigate(CipherType.Login); + jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValue(false); + jest.spyOn(Utils, "getHostname").mockReturnValue("example.com"); - expect(navigate).toHaveBeenCalledWith(["/add-cipher"], { - queryParams: { - type: CipherType.Login.toString(), - folderId: "222-333-444", - organizationId: "444-555-666", - collectionId: "777-888-999", - uri: "https://example.com", - name: "example.com", - }, + const params = component.buildQueryParams(CipherType.Login); + + expect(params).toEqual({ + type: CipherType.Login.toString(), + collectionId: "777-888-999", + organizationId: "444-555-666", + folderId: "222-333-444", + uri: "https://example.com", + name: "example.com", }); }); - it("does not include name or uri when the extension is popped out", async () => { + it("should build query params for a Login cipher when popped out", () => { + component.initialValues = { + collectionId: "777-888-999", + } as NewItemInitialValues; + jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValue(true); + const params = component.buildQueryParams(CipherType.Login); + + expect(params).toEqual({ + type: CipherType.Login.toString(), + collectionId: "777-888-999", + }); + }); + + it("should build query params for a secure note", () => { component.initialValues = { - folderId: "222-333-444", - organizationId: "444-555-666", collectionId: "777-888-999", } as NewItemInitialValues; - await component.newItemNavigate(CipherType.Login); + const params = component.buildQueryParams(CipherType.SecureNote); - expect(navigate).toHaveBeenCalledWith(["/add-cipher"], { - queryParams: { - type: CipherType.Login.toString(), - folderId: "222-333-444", - organizationId: "444-555-666", - collectionId: "777-888-999", - }, + expect(params).toEqual({ + type: CipherType.SecureNote.toString(), + collectionId: "777-888-999", + }); + }); + + it("should build query params for an Identity", () => { + component.initialValues = { + collectionId: "777-888-999", + } as NewItemInitialValues; + + const params = component.buildQueryParams(CipherType.Identity); + + expect(params).toEqual({ + type: CipherType.Identity.toString(), + collectionId: "777-888-999", + }); + }); + + it("should build query params for a Card", () => { + component.initialValues = { + collectionId: "777-888-999", + } as NewItemInitialValues; + + const params = component.buildQueryParams(CipherType.Card); + + expect(params).toEqual({ + type: CipherType.Card.toString(), + collectionId: "777-888-999", + }); + }); + + it("should build query params for a SshKey", () => { + component.initialValues = { + collectionId: "777-888-999", + } as NewItemInitialValues; + + const params = component.buildQueryParams(CipherType.SshKey); + + expect(params).toEqual({ + type: CipherType.SshKey.toString(), + collectionId: "777-888-999", }); }); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts index a1d5cbd332..e2062101e1 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts @@ -1,6 +1,6 @@ import { CommonModule } from "@angular/common"; -import { Component, Input } from "@angular/core"; -import { Router, RouterLink } from "@angular/router"; +import { Component, Input, OnInit } from "@angular/core"; +import { RouterLink } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -25,31 +25,31 @@ export interface NewItemInitialValues { standalone: true, imports: [NoItemsModule, JslibModule, CommonModule, ButtonModule, RouterLink, MenuModule], }) -export class NewItemDropdownV2Component { +export class NewItemDropdownV2Component implements OnInit { cipherType = CipherType; - + private tab?: chrome.tabs.Tab; /** * Optional initial values to pass to the add cipher form */ @Input() initialValues: NewItemInitialValues; - constructor( - private router: Router, - private dialogService: DialogService, - ) {} + constructor(private dialogService: DialogService) {} - private async buildQueryParams(type: CipherType): Promise { - const tab = await BrowserApi.getTabFromCurrentWindow(); + async ngOnInit() { + this.tab = await BrowserApi.getTabFromCurrentWindow(); + } + + buildQueryParams(type: CipherType): AddEditQueryParams { const poppedOut = BrowserPopupUtils.inPopout(window); const loginDetails: { uri?: string; name?: string } = {}; // When a Login Cipher is created and the extension is not popped out, // pass along the uri and name - if (!poppedOut && type === CipherType.Login && tab) { - loginDetails.uri = tab.url; - loginDetails.name = Utils.getHostname(tab.url); + if (!poppedOut && type === CipherType.Login && this.tab) { + loginDetails.uri = this.tab.url; + loginDetails.name = Utils.getHostname(this.tab.url); } return { @@ -61,10 +61,6 @@ export class NewItemDropdownV2Component { }; } - async newItemNavigate(type: CipherType) { - await this.router.navigate(["/add-cipher"], { queryParams: await this.buildQueryParams(type) }); - } - openFolderDialog() { this.dialogService.open(AddEditFolderDialogComponent); } diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html index 05deeec0d3..5f958433c6 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.html @@ -32,7 +32,5 @@ [open]="initialDisclosureVisibility$ | async" (openChange)="toggleFilters($event)" > - - - + diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts index 36343d3a66..bc84dd337c 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts @@ -5,7 +5,12 @@ import { Subject } from "rxjs"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AUTOFILL_ID } from "@bitwarden/common/autofill/constants"; +import { + AUTOFILL_ID, + COPY_PASSWORD_ID, + COPY_USERNAME_ID, + COPY_VERIFICATION_CODE_ID, +} from "@bitwarden/common/autofill/constants"; import { EventType } from "@bitwarden/common/enums"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -17,7 +22,10 @@ import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { CopyCipherFieldService } from "@bitwarden/vault"; +import { BrowserApi } from "../../../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service"; import { VaultPopupAutofillService } from "./../../../services/vault-popup-autofill.service"; @@ -34,17 +42,26 @@ describe("ViewV2Component", () => { const params$ = new Subject(); const mockNavigate = jest.fn(); const collect = jest.fn().mockResolvedValue(null); - const doAutofill = jest.fn(); + const doAutofill = jest.fn().mockResolvedValue(true); + const copy = jest.fn().mockResolvedValue(true); const mockCipher = { id: "122-333-444", type: CipherType.Login, orgId: "222-444-555", + login: { + username: "test-username", + password: "test-password", + totp: "123", + }, }; const mockVaultPopupAutofillService = { doAutofill, }; + const mockCopyCipherFieldService = { + copy, + }; const mockUserId = Utils.newGuid() as UserId; const accountService: FakeAccountService = mockAccountServiceWith(mockUserId); @@ -57,6 +74,7 @@ describe("ViewV2Component", () => { mockNavigate.mockClear(); collect.mockClear(); doAutofill.mockClear(); + copy.mockClear(); await TestBed.configureTestingModule({ imports: [ViewV2Component], @@ -91,6 +109,10 @@ describe("ViewV2Component", () => { canDeleteCipher$: jest.fn().mockReturnValue(true), }, }, + { + provide: CopyCipherFieldService, + useValue: mockCopyCipherFieldService, + }, ], }).compileComponents(); @@ -159,5 +181,46 @@ describe("ViewV2Component", () => { expect(doAutofill).toHaveBeenCalledOnce(); })); + + it('invokes `copy` when action="copy-username"', fakeAsync(() => { + params$.next({ action: COPY_USERNAME_ID }); + + flush(); // Resolve all promises + + expect(copy).toHaveBeenCalledOnce(); + })); + + it('invokes `copy` when action="copy-password"', fakeAsync(() => { + params$.next({ action: COPY_PASSWORD_ID }); + + flush(); // Resolve all promises + + expect(copy).toHaveBeenCalledOnce(); + })); + + it('invokes `copy` when action="copy-totp"', fakeAsync(() => { + params$.next({ action: COPY_VERIFICATION_CODE_ID }); + + flush(); // Resolve all promises + + expect(copy).toHaveBeenCalledOnce(); + })); + + it("closes the popout after a load action", fakeAsync(() => { + jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValueOnce(true); + jest.spyOn(BrowserPopupUtils, "inSingleActionPopout").mockReturnValueOnce(true); + const closeSpy = jest.spyOn(BrowserPopupUtils, "closeSingleActionPopout"); + const focusSpy = jest + .spyOn(BrowserApi, "focusTab") + .mockImplementation(() => Promise.resolve()); + + params$.next({ action: AUTOFILL_ID, senderTabId: 99 }); + + flush(); // Resolve all promises + + expect(doAutofill).toHaveBeenCalledOnce(); + expect(focusSpy).toHaveBeenCalledWith(99); + expect(closeSpy).toHaveBeenCalledOnce(); + })); }); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 8242fd8747..f739c0ce82 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -10,7 +10,13 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AUTOFILL_ID, SHOW_AUTOFILL_BUTTON } from "@bitwarden/common/autofill/constants"; +import { + AUTOFILL_ID, + COPY_PASSWORD_ID, + COPY_USERNAME_ID, + COPY_VERIFICATION_CODE_ID, + SHOW_AUTOFILL_BUTTON, +} from "@bitwarden/common/autofill/constants"; import { EventType } from "@bitwarden/common/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -18,7 +24,6 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { AsyncActionsModule, @@ -28,19 +33,34 @@ import { SearchModule, ToastService, } from "@bitwarden/components"; +import { CopyCipherFieldService } from "@bitwarden/vault"; import { PremiumUpgradePromptService } from "../../../../../../../../libs/common/src/vault/abstractions/premium-upgrade-prompt.service"; import { CipherViewComponent } from "../../../../../../../../libs/vault/src/cipher-view"; +import { BrowserApi } from "../../../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; import { PopOutComponent } from "../../../../../platform/popup/components/pop-out.component"; import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service"; import { BrowserPremiumUpgradePromptService } from "../../../services/browser-premium-upgrade-prompt.service"; import { BrowserViewPasswordHistoryService } from "../../../services/browser-view-password-history.service"; +import { closeViewVaultItemPopout, VaultPopoutType } from "../../../utils/vault-popout-window"; import { PopupFooterComponent } from "./../../../../../platform/popup/layout/popup-footer.component"; import { PopupHeaderComponent } from "./../../../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "./../../../../../platform/popup/layout/popup-page.component"; import { VaultPopupAutofillService } from "./../../../services/vault-popup-autofill.service"; +/** + * The types of actions that can be triggered when loading the view vault item popout via the + * extension ContextMenu. See context-menu-clicked-handler.ts for more information. + */ +type LoadAction = + | typeof AUTOFILL_ID + | typeof SHOW_AUTOFILL_BUTTON + | typeof COPY_USERNAME_ID + | typeof COPY_PASSWORD_ID + | typeof COPY_VERIFICATION_CODE_ID; + @Component({ selector: "app-view-v2", templateUrl: "view-v2.component.html", @@ -68,10 +88,10 @@ export class ViewV2Component { headerText: string; cipher: CipherView; organization$: Observable; - folder$: Observable; canDeleteCipher$: Observable; collections$: Observable; - loadAction: typeof AUTOFILL_ID | typeof SHOW_AUTOFILL_BUTTON; + loadAction: LoadAction; + senderTabId?: number; constructor( private route: ActivatedRoute, @@ -86,6 +106,7 @@ export class ViewV2Component { private eventCollectionService: EventCollectionService, private popupRouterCacheService: PopupRouterCacheService, protected cipherAuthorizationService: CipherAuthorizationService, + private copyCipherFieldService: CopyCipherFieldService, ) { this.subscribeToParams(); } @@ -95,13 +116,15 @@ export class ViewV2Component { .pipe( switchMap(async (params): Promise => { this.loadAction = params.action; + this.senderTabId = params.senderTabId ? parseInt(params.senderTabId, 10) : undefined; return await this.getCipherData(params.cipherId); }), switchMap(async (cipher) => { this.cipher = cipher; this.headerText = this.setHeader(cipher.type); - if (this.loadAction === AUTOFILL_ID) { - await this.vaultPopupAutofillService.doAutofill(this.cipher); + + if (this.loadAction) { + await this._handleLoadAction(this.loadAction, this.senderTabId); } this.canDeleteCipher$ = this.cipherAuthorizationService.canDeleteCipher$(cipher); @@ -211,4 +234,65 @@ export class ViewV2Component { protected showFooter(): boolean { return this.cipher && (!this.cipher.isDeleted || (this.cipher.isDeleted && this.cipher.edit)); } + + /** + * Handles the load action for the view vault item popout. These actions are typically triggered + * via the extension context menu. It is necessary to render the view for items that have password + * reprompt enabled. + * @param loadAction + * @param senderTabId + * @private + */ + private async _handleLoadAction(loadAction: LoadAction, senderTabId?: number): Promise { + let actionSuccess = false; + + // Both vaultPopupAutofillService and copyCipherFieldService will perform password re-prompting internally. + + switch (loadAction) { + case "show-autofill-button": + // This action simply shows the cipher view, no need to do anything. + return; + case "autofill": + actionSuccess = await this.vaultPopupAutofillService.doAutofill(this.cipher, false); + break; + case "copy-username": + actionSuccess = await this.copyCipherFieldService.copy( + this.cipher.login.username, + "username", + this.cipher, + ); + break; + case "copy-password": + actionSuccess = await this.copyCipherFieldService.copy( + this.cipher.login.password, + "password", + this.cipher, + ); + break; + case "copy-totp": + actionSuccess = await this.copyCipherFieldService.copy( + this.cipher.login.totp, + "totp", + this.cipher, + ); + break; + } + + if (BrowserPopupUtils.inPopout(window)) { + setTimeout( + async () => { + if ( + BrowserPopupUtils.inSingleActionPopout(window, VaultPopoutType.viewVaultItem) && + senderTabId + ) { + await BrowserApi.focusTab(senderTabId); + await closeViewVaultItemPopout(`${VaultPopoutType.viewVaultItem}_${this.cipher.id}`); + } else { + await this.popupRouterCacheService.back(); + } + }, + actionSuccess ? 1000 : 0, + ); + } + } } diff --git a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts index 0f76c2f2cd..7620bd4950 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts @@ -10,7 +10,6 @@ import { startWith, Subject, switchMap, - timeout, } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -75,9 +74,7 @@ export class VaultPopupAutofillService { if (!tab) { return of([]); } - return this.autofillService - .collectPageDetailsFromTab$(tab) - .pipe(timeout({ first: 1500, with: () => of([]) })); + return this.autofillService.collectPageDetailsFromTab$(tab); }), shareReplay({ refCount: false, bufferSize: 1 }), ); diff --git a/apps/browser/src/vault/popup/services/vault-ui-onboarding.service.ts b/apps/browser/src/vault/popup/services/vault-ui-onboarding.service.ts index 151f8517d5..2b37b26b9c 100644 --- a/apps/browser/src/vault/popup/services/vault-ui-onboarding.service.ts +++ b/apps/browser/src/vault/popup/services/vault-ui-onboarding.service.ts @@ -24,8 +24,7 @@ export const GLOBAL_VAULT_UI_ONBOARDING = new KeyDefinition( @Injectable() export class VaultUiOnboardingService { - // TODO: Update this date to the release date of the new Browser UI - private onboardingUiReleaseDate = new Date("2024-07-25"); + private onboardingUiReleaseDate = new Date("2024-12-10"); private vaultUiOnboardingState: GlobalState = this.stateProvider.getGlobal( GLOBAL_VAULT_UI_ONBOARDING, diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.html b/apps/browser/src/vault/popup/settings/appearance-v2.component.html index 466cffa216..0fc6297b50 100644 --- a/apps/browser/src/vault/popup/settings/appearance-v2.component.html +++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.html @@ -18,6 +18,11 @@ + + {{ "extensionWidth" | i18n }} + + + { const setEnableRoutingAnimation = jest.fn().mockResolvedValue(undefined); const setEnableCompactMode = jest.fn().mockResolvedValue(undefined); + const mockWidthService: Partial = { + width$: new BehaviorSubject("default"), + setWidth: jest.fn().mockResolvedValue(undefined), + }; + beforeEach(async () => { setSelectedTheme.mockClear(); setShowFavicons.mockClear(); @@ -78,6 +84,10 @@ describe("AppearanceV2Component", () => { provide: PopupCompactModeService, useValue: { enabled$: enableCompactMode$, setEnabled: setEnableCompactMode }, }, + { + provide: PopupWidthService, + useValue: mockWidthService, + }, ], }) .overrideComponent(AppearanceV2Component, { @@ -102,6 +112,7 @@ describe("AppearanceV2Component", () => { enableBadgeCounter: true, theme: ThemeType.Nord, enableCompactMode: false, + width: "default", }); }); diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts index c20bc48bfe..31f7f31148 100644 --- a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts @@ -12,7 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; -import { BadgeModule, CheckboxModule } from "@bitwarden/components"; +import { BadgeModule, CheckboxModule, Option } from "@bitwarden/components"; import { CardComponent } from "../../../../../../libs/components/src/card/card.component"; import { FormFieldModule } from "../../../../../../libs/components/src/form-field/form-field.module"; @@ -21,6 +21,10 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupCompactModeService } from "../../../platform/popup/layout/popup-compact-mode.service"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; +import { + PopupWidthOption, + PopupWidthService, +} from "../../../platform/popup/layout/popup-width.service"; @Component({ standalone: true, @@ -41,6 +45,8 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co }) export class AppearanceV2Component implements OnInit { private compactModeService = inject(PopupCompactModeService); + private popupWidthService = inject(PopupWidthService); + private i18nService = inject(I18nService); appearanceForm = this.formBuilder.group({ enableFavicon: false, @@ -48,6 +54,7 @@ export class AppearanceV2Component implements OnInit { theme: ThemeType.System, enableAnimations: true, enableCompactMode: false, + width: "default" as PopupWidthOption, }); /** To avoid flashes of inaccurate values, only show the form after the entire form is populated. */ @@ -56,6 +63,13 @@ export class AppearanceV2Component implements OnInit { /** Available theme options */ themeOptions: { name: string; value: ThemeType }[]; + /** Available width options */ + protected readonly widthOptions: Option[] = [ + { label: this.i18nService.t("default"), value: "default" }, + { label: this.i18nService.t("wide"), value: "wide" }, + { label: this.i18nService.t("extraWide"), value: "extra-wide" }, + ]; + constructor( private messagingService: MessagingService, private domainSettingsService: DomainSettingsService, @@ -81,6 +95,7 @@ export class AppearanceV2Component implements OnInit { this.animationControlService.enableRoutingAnimation$, ); const enableCompactMode = await firstValueFrom(this.compactModeService.enabled$); + const width = await firstValueFrom(this.popupWidthService.width$); // Set initial values for the form this.appearanceForm.setValue({ @@ -89,6 +104,7 @@ export class AppearanceV2Component implements OnInit { theme, enableAnimations, enableCompactMode, + width, }); this.formLoading = false; @@ -122,6 +138,12 @@ export class AppearanceV2Component implements OnInit { .subscribe((enableCompactMode) => { void this.updateCompactMode(enableCompactMode); }); + + this.appearanceForm.controls.width.valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((width) => { + void this.updateWidth(width); + }); } async updateFavicon(enableFavicon: boolean) { @@ -144,4 +166,8 @@ export class AppearanceV2Component implements OnInit { async updateCompactMode(enableCompactMode: boolean) { await this.compactModeService.setEnabled(enableCompactMode); } + + async updateWidth(width: PopupWidthOption) { + await this.popupWidthService.setWidth(width); + } } diff --git a/apps/browser/store/locales/ar/copy.resx b/apps/browser/store/locales/ar/copy.resx index e1bfa48b44..9fdfb94210 100644 --- a/apps/browser/store/locales/ar/copy.resx +++ b/apps/browser/store/locales/ar/copy.resx @@ -172,16 +172,16 @@ End-to-end encrypted credential management solutions from Bitwarden empower orga At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. - مزامنة خزنتك والوصول إليها من عدة أجهزة + مزامنة خزانتك والوصول إليها من عدة أجهزة - إدارة جميع تسجيلات الدخول وكلمات المرور الخاصة بك من خزنة آمنة + إدارة جميع تسجيلات الدخول وكلمات المرور الخاصة بك من خزانة آمنة قم بملء بيانات تسجيل الدخول تلقائياً وبسرعة في أي موقع تزوره - يمكنك الوصول الى خزنتك بسهولة من قائمة النقر بالزر الأيمن + يمكنك الوصول الى خزانتك بسهولة من قائمة النقر بالزر الأيمن إنشاء كلمات مرور قوية، وعشوائية، وآمنة، تلقائيًا diff --git a/apps/cli/package.json b/apps/cli/package.json index 8be8f9ca34..16ad1c8519 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/cli", "description": "A secure and free password manager for all of your devices.", - "version": "2024.11.1", + "version": "2024.12.0", "keywords": [ "bitwarden", "password", @@ -80,7 +80,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.61", + "tldts": "6.1.64", "zxcvbn": "4.4.2" } } diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index fc014534e0..3f8702d000 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -21,7 +21,6 @@ import { LoginExport } from "@bitwarden/common/models/export/login.export"; import { SecureNoteExport } from "@bitwarden/common/models/export/secure-note.export"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; -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 { SendType } from "@bitwarden/common/tools/send/enums/send-type"; @@ -58,7 +57,6 @@ export class GetCommand extends DownloadCommand { private auditService: AuditService, private keyService: KeyService, encryptService: EncryptService, - private stateService: StateService, private searchService: SearchService, private apiService: ApiService, private organizationService: OrganizationService, diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts index a25357f6f6..0614aa05e1 100644 --- a/apps/cli/src/oss-serve-configurator.ts +++ b/apps/cli/src/oss-serve-configurator.ts @@ -58,7 +58,6 @@ export class OssServeConfigurator { this.serviceContainer.auditService, this.serviceContainer.keyService, this.serviceContainer.encryptService, - this.serviceContainer.stateService, this.serviceContainer.searchService, this.serviceContainer.apiService, this.serviceContainer.organizationService, diff --git a/apps/cli/src/tools/generate.command.ts b/apps/cli/src/tools/generate.command.ts index 2829bc5141..0a17e4843b 100644 --- a/apps/cli/src/tools/generate.command.ts +++ b/apps/cli/src/tools/generate.command.ts @@ -33,7 +33,7 @@ export class GenerateCommand { includeNumber: normalizedOptions.includeNumber, minNumber: normalizedOptions.minNumber, minSpecial: normalizedOptions.minSpecial, - ambiguous: normalizedOptions.ambiguous, + ambiguous: !normalizedOptions.ambiguous, }; const enforcedOptions = (await this.stateService.getIsAuthenticated()) diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts index 60e78137e7..d70b810221 100644 --- a/apps/cli/src/tools/send/send.program.ts +++ b/apps/cli/src/tools/send/send.program.ts @@ -144,7 +144,6 @@ export class SendProgram extends BaseProgram { this.serviceContainer.auditService, this.serviceContainer.keyService, this.serviceContainer.encryptService, - this.serviceContainer.stateService, this.serviceContainer.searchService, this.serviceContainer.apiService, this.serviceContainer.organizationService, diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts index 4d3215944e..252818c2f2 100644 --- a/apps/cli/src/vault.program.ts +++ b/apps/cli/src/vault.program.ts @@ -179,7 +179,6 @@ export class VaultProgram extends BaseProgram { this.serviceContainer.auditService, this.serviceContainer.keyService, this.serviceContainer.encryptService, - this.serviceContainer.stateService, this.serviceContainer.searchService, this.serviceContainer.apiService, this.serviceContainer.organizationService, diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index 53d151397f..16a5a48cd0 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -211,9 +211,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -222,9 +222,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -298,12 +298,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -313,7 +307,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitwarden-russh" version = "0.1.0" -source = "git+https://github.com/bitwarden/bitwarden-russh.git?branch=km/pm-10098/clean-russh-implementation#86ff1bf2f4620a3ae5684adee31abdbee33c6f07" +source = "git+https://github.com/bitwarden/bitwarden-russh.git?rev=b4e7f2fedbe3df8c35545feb000176d3e7b2bc32#b4e7f2fedbe3df8c35545feb000176d3e7b2bc32" dependencies = [ "anyhow", "byteorder", @@ -701,7 +695,7 @@ dependencies = [ "tokio-util", "typenum", "widestring", - "windows", + "windows 0.58.0", "zbus", "zbus_polkit", ] @@ -1099,7 +1093,7 @@ version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44" dependencies = [ - "bitflags 2.6.0", + "bitflags", "futures-channel", "futures-core", "futures-executor", @@ -1190,14 +1184,14 @@ dependencies = [ [[package]] name = "homedir" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bed305c13ce3829a09d627f5d43ff738482a09361ae4eb8039993b55fb10e5e" +checksum = "5bdbbd5bc8c5749697ccaa352fa45aff8730cf21c68029c0eef1ffed7c3d6ba2" dependencies = [ "cfg-if", - "nix 0.26.4", + "nix 0.29.0", "widestring", - "windows", + "windows 0.57.0", ] [[package]] @@ -1299,7 +1293,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags", "libc", ] @@ -1366,15 +1360,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -1417,7 +1402,7 @@ version = "2.16.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b" dependencies = [ - "bitflags 2.6.0", + "bitflags", "ctor", "napi-derive", "napi-sys", @@ -1469,26 +1454,13 @@ dependencies = [ "libloading", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", - "pin-utils", -] - [[package]] name = "nix" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -1500,11 +1472,11 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "cfg_aliases 0.2.1", "libc", - "memoffset 0.9.1", + "memoffset", ] [[package]] @@ -1601,7 +1573,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags", "block2", "libc", "objc2", @@ -1617,7 +1589,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags", "block2", "objc2", "objc2-foundation", @@ -1647,7 +1619,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags", "block2", "libc", "objc2", @@ -1659,7 +1631,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags", "block2", "objc2", "objc2-foundation", @@ -1671,7 +1643,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags", "block2", "objc2", "objc2-foundation", @@ -1785,18 +1757,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -2001,7 +1973,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] @@ -2105,7 +2077,7 @@ version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -2156,7 +2128,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d0283c0a4a22a0f1b0e4edca251aa20b92fc96eaa09b84bec052f9415e9d71" dependencies = [ - "bitflags 2.6.0", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -2348,9 +2320,9 @@ dependencies = [ [[package]] name = "ssh-key" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9b366a80cf18bb6406f4cf4d10aebfb46140a8c0c33f666a144c5c76ecbafc" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" dependencies = [ "bcrypt-pbkdf", "ed25519-dalek", @@ -2484,9 +2456,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", @@ -2623,7 +2595,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "winapi", ] @@ -2694,7 +2666,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.6.0", + "bitflags", "rustix", "wayland-backend", "wayland-scanner", @@ -2706,7 +2678,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.6.0", + "bitflags", "wayland-backend", "wayland-client", "wayland-scanner", @@ -2718,7 +2690,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.6.0", + "bitflags", "wayland-backend", "wayland-client", "wayland-protocols", @@ -2790,7 +2762,17 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "windows-core", + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -2800,12 +2782,25 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.57.0", + "windows-interface 0.57.0", "windows-result 0.1.2", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -2817,6 +2812,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.57.0" @@ -2828,6 +2834,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-registry" version = "0.3.0" @@ -2835,7 +2852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bafa604f2104cf5ae2cc2db1dee84b7e6a5d11b05f737b60def0ffdc398cbc0a" dependencies = [ "windows-result 0.2.0", - "windows-strings", + "windows-strings 0.2.0", "windows-targets 0.52.6", ] @@ -2857,6 +2874,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-strings" version = "0.2.0" diff --git a/apps/desktop/desktop_native/core/Cargo.toml b/apps/desktop/desktop_native/core/Cargo.toml index 039cd19c6a..6abf7fbb0a 100644 --- a/apps/desktop/desktop_native/core/Cargo.toml +++ b/apps/desktop/desktop_native/core/Cargo.toml @@ -10,30 +10,30 @@ default = ["sys"] manual_test = [] sys = [ - "dep:widestring", - "dep:windows", - "dep:core-foundation", - "dep:security-framework", - "dep:security-framework-sys", - "dep:gio", - "dep:libsecret", - "dep:zbus", - "dep:zbus_polkit", + "dep:widestring", + "dep:windows", + "dep:core-foundation", + "dep:security-framework", + "dep:security-framework-sys", + "dep:gio", + "dep:libsecret", + "dep:zbus", + "dep:zbus_polkit", ] [dependencies] aes = "=0.8.4" anyhow = "=1.0.93" arboard = { version = "=3.4.1", default-features = false, features = [ - "wayland-data-control", + "wayland-data-control", ] } -async-stream = "=0.3.5" +async-stream = "=0.3.6" base64 = "=0.22.1" byteorder = "=1.5.0" cbc = { version = "=0.1.2", features = ["alloc"] } -homedir = "=0.3.3" +homedir = "=0.3.4" libc = "=0.2.162" -pin-project = "=1.1.5" +pin-project = "=1.1.7" dirs = "=5.0.1" futures = "=0.3.31" interprocess = { version = "=2.2.1", features = ["tokio"] } @@ -44,16 +44,16 @@ russh-cryptovec = "=0.7.3" scopeguard = "=1.2.0" sha2 = "=0.10.8" ssh-encoding = "=0.2.0" -ssh-key = { version = "=0.6.6", default-features = false, features = [ +ssh-key = { version = "=0.6.7", default-features = false, features = [ "encryption", "ed25519", "rsa", "getrandom", ] } -bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", branch = "km/pm-10098/clean-russh-implementation" } -tokio = { version = "=1.40.0", features = ["io-util", "sync", "macros", "net"] } +bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "b4e7f2fedbe3df8c35545feb000176d3e7b2bc32" } +tokio = { version = "=1.41.1", features = ["io-util", "sync", "macros", "net"] } tokio-stream = { version = "=0.1.15", features = ["net"] } -tokio-util = { version = "0.7.11", features = ["codec"] } +tokio-util = { version = "=0.7.12", features = ["codec"] } thiserror = "=1.0.69" typenum = "=1.17.0" rand_chacha = "=0.3.1" @@ -63,7 +63,7 @@ ed25519 = { version = "=2.2.3", features = ["pkcs8"] } [target.'cfg(windows)'.dependencies] widestring = { version = "=1.1.0", optional = true } -windows = { version = "=0.57.0", features = [ +windows = { version = "=0.58.0", features = [ "Foundation", "Security_Credentials_UI", "Security_Cryptography", diff --git a/apps/desktop/desktop_native/core/src/biometric/windows.rs b/apps/desktop/desktop_native/core/src/biometric/windows.rs index d5e8b6dc91..c951e42e26 100644 --- a/apps/desktop/desktop_native/core/src/biometric/windows.rs +++ b/apps/desktop/desktop_native/core/src/biometric/windows.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{ffi::c_void, str::FromStr}; use anyhow::{anyhow, Result}; use base64::{engine::general_purpose::STANDARD as base64_engine, Engine}; @@ -40,6 +40,8 @@ pub struct Biometric {} impl super::BiometricTrait for Biometric { async fn prompt(hwnd: Vec, message: String) -> Result { let h = isize::from_le_bytes(hwnd.clone().try_into().unwrap()); + + let h = h as *mut c_void; let window = HWND(h); // The Windows Hello prompt is displayed inside the application window. For best result we @@ -174,7 +176,7 @@ fn focus_security_prompt() -> Result<()> { class_name: windows::core::PCSTR, ) -> retry::OperationResult<(), ()> { let hwnd = unsafe { FindWindowA(class_name, None) }; - if hwnd.0 != 0 { + if let Ok(hwnd) = hwnd { set_focus(hwnd); return retry::OperationResult::Ok(()); } diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs b/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs index 8cbcd97d91..9d04ea87cc 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs @@ -17,9 +17,11 @@ pub mod importer; pub struct BitwardenDesktopAgent { keystore: ssh_agent::KeyStore, cancellation_token: CancellationToken, - show_ui_request_tx: tokio::sync::mpsc::Sender<(u32, String)>, + show_ui_request_tx: tokio::sync::mpsc::Sender<(u32, (String, bool))>, get_ui_response_rx: Arc>>, request_id: Arc>, + /// before first unlock, or after account switching, listing keys should require an unlock to get a list of public keys + needs_unlock: Arc>, is_running: Arc>, } @@ -33,8 +35,30 @@ impl ssh_agent::Agent for BitwardenDesktopAgent { let request_id = self.get_request_id().await; let mut rx_channel = self.get_ui_response_rx.lock().await.resubscribe(); + let message = (request_id, (ssh_key.cipher_uuid.clone(), false)); self.show_ui_request_tx - .send((request_id, ssh_key.cipher_uuid.clone())) + .send(message) + .await + .expect("Should send request to ui"); + while let Ok((id, response)) = rx_channel.recv().await { + if id == request_id { + return response; + } + } + false + } + + async fn can_list(&self) -> bool { + if !*self.needs_unlock.lock().await{ + return true; + } + + let request_id = self.get_request_id().await; + + let mut rx_channel = self.get_ui_response_rx.lock().await.resubscribe(); + let message = (request_id, ("".to_string(), true)); + self.show_ui_request_tx + .send(message) .await .expect("Should send request to ui"); while let Ok((id, response)) = rx_channel.recv().await { @@ -75,6 +99,8 @@ impl BitwardenDesktopAgent { let keystore = &mut self.keystore; keystore.0.write().expect("RwLock is not poisoned").clear(); + *self.needs_unlock.blocking_lock() = false; + for (key, name, cipher_id) in new_keys.iter() { match parse_key_safe(&key) { Ok(private_key) => { @@ -119,6 +145,14 @@ impl BitwardenDesktopAgent { Ok(()) } + pub fn clear_keys(&mut self) -> Result<(), anyhow::Error> { + let keystore = &mut self.keystore; + keystore.0.write().expect("RwLock is not poisoned").clear(); + *self.needs_unlock.blocking_lock() = true; + + Ok(()) + } + async fn get_request_id(&self) -> u32 { if !*self.is_running.lock().await { println!("[BitwardenDesktopAgent] Agent is not running, but tried to get request id"); diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs b/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs index 27f27998ce..ed2fe9ffab 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs @@ -14,7 +14,7 @@ use super::BitwardenDesktopAgent; impl BitwardenDesktopAgent { pub async fn start_server( - auth_request_tx: tokio::sync::mpsc::Sender<(u32, String)>, + auth_request_tx: tokio::sync::mpsc::Sender<(u32, (String, bool))>, auth_response_rx: Arc>>, ) -> Result { let agent = BitwardenDesktopAgent { @@ -23,6 +23,7 @@ impl BitwardenDesktopAgent { show_ui_request_tx: auth_request_tx, get_ui_response_rx: auth_response_rx, request_id: Arc::new(tokio::sync::Mutex::new(0)), + needs_unlock: Arc::new(tokio::sync::Mutex::new(true)), is_running: Arc::new(tokio::sync::Mutex::new(false)), }; let cloned_agent_state = agent.clone(); diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs b/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs index 9ccae7af67..6a99b7cfb0 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs @@ -12,7 +12,7 @@ use super::BitwardenDesktopAgent; impl BitwardenDesktopAgent { pub async fn start_server( - auth_request_tx: tokio::sync::mpsc::Sender<(u32, String)>, + auth_request_tx: tokio::sync::mpsc::Sender<(u32, (String, bool))>, auth_response_rx: Arc>>, ) -> Result { let agent_state = BitwardenDesktopAgent { @@ -21,6 +21,7 @@ impl BitwardenDesktopAgent { get_ui_response_rx: auth_response_rx, cancellation_token: CancellationToken::new(), request_id: Arc::new(tokio::sync::Mutex::new(0)), + needs_unlock: Arc::new(tokio::sync::Mutex::new(true)), is_running: Arc::new(tokio::sync::Mutex::new(true)), }; let stream = named_pipe_listener_stream::NamedPipeServerStream::new( diff --git a/apps/desktop/desktop_native/napi/Cargo.toml b/apps/desktop/desktop_native/napi/Cargo.toml index bf7701a656..644ff8a51d 100644 --- a/apps/desktop/desktop_native/napi/Cargo.toml +++ b/apps/desktop/desktop_native/napi/Cargo.toml @@ -20,7 +20,7 @@ anyhow = "=1.0.93" desktop_core = { path = "../core" } napi = { version = "=2.16.13", features = ["async"] } napi-derive = "=2.16.12" -tokio = { version = "=1.40.0" } +tokio = { version = "=1.41.1" } tokio-util = "=0.7.12" tokio-stream = "=0.1.15" diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index 62e448d480..956b2d726d 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -69,12 +69,13 @@ export declare namespace sshagent { status: SshKeyImportStatus sshKey?: SshKey } - export function serve(callback: (err: Error | null, arg: string) => any): Promise + export function serve(callback: (err: Error | null, arg0: string, arg1: boolean) => any): Promise export function stop(agentState: SshAgentState): void export function isRunning(agentState: SshAgentState): boolean export function setKeys(agentState: SshAgentState, newKeys: Array): void export function lock(agentState: SshAgentState): void export function importKey(encodedKey: string, password: string): SshKeyImportResult + export function clearKeys(agentState: SshAgentState): void export function generateKeypair(keyAlgorithm: string): Promise export class SshAgentState { } } diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 8e156eb3ef..4c7bc8eaa9 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -247,15 +247,15 @@ pub mod sshagent { #[napi] pub async fn serve( - callback: ThreadsafeFunction, + callback: ThreadsafeFunction<(String, bool), CalleeHandled>, ) -> napi::Result { - let (auth_request_tx, mut auth_request_rx) = tokio::sync::mpsc::channel::<(u32, String)>(32); + let (auth_request_tx, mut auth_request_rx) = tokio::sync::mpsc::channel::<(u32, (String, bool))>(32); let (auth_response_tx, auth_response_rx) = tokio::sync::broadcast::channel::<(u32, bool)>(32); let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx)); tokio::spawn(async move { let _ = auth_response_rx; - while let Some((request_id, cipher_uuid)) = auth_request_rx.recv().await { + while let Some((request_id, (cipher_uuid, is_list_request))) = auth_request_rx.recv().await { let cloned_request_id = request_id.clone(); let cloned_cipher_uuid = cipher_uuid.clone(); let cloned_response_tx_arc = auth_response_tx_arc.clone(); @@ -266,7 +266,7 @@ pub mod sshagent { let auth_response_tx_arc = cloned_response_tx_arc; let callback = cloned_callback; let promise_result: Result, napi::Error> = - callback.call_async(Ok(cipher_uuid)).await; + callback.call_async(Ok((cipher_uuid, is_list_request))).await; match promise_result { Ok(promise_result) => match promise_result.await { Ok(result) => { @@ -345,6 +345,12 @@ pub mod sshagent { Ok(result.into()) } + #[napi] + pub fn clear_keys(agent_state: &mut SshAgentState) -> napi::Result<()> { + let bitwarden_agent_state = &mut agent_state.state; + bitwarden_agent_state.clear_keys().map_err(|e| napi::Error::from_reason(e.to_string())) + } + #[napi] pub async fn generate_keypair(key_algorithm: String) -> napi::Result { desktop_core::ssh_agent::generator::generate_keypair(key_algorithm) diff --git a/apps/desktop/desktop_native/proxy/Cargo.toml b/apps/desktop/desktop_native/proxy/Cargo.toml index 0208b2f033..0511f583c1 100644 --- a/apps/desktop/desktop_native/proxy/Cargo.toml +++ b/apps/desktop/desktop_native/proxy/Cargo.toml @@ -12,7 +12,7 @@ desktop_core = { path = "../core", default-features = false } futures = "=0.3.31" log = "=0.4.22" simplelog = "=0.12.2" -tokio = { version = "=1.40.0", features = ["io-std", "io-util", "macros", "rt"] } +tokio = { version = "=1.41.1", features = ["io-std", "io-util", "macros", "rt"] } tokio-util = { version = "=0.7.12", features = ["codec"] } [target.'cfg(target_os = "macos")'.dependencies] diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index f51c0a32d9..8dd8204b69 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -18,7 +18,7 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.9.0", + "@types/node": "22.9.3", "@types/node-ipc": "9.2.3", "typescript": "4.7.4" } @@ -106,9 +106,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz", + "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==", "license": "MIT", "dependencies": { "undici-types": "~6.19.8" diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 23b1dbdafb..d2da248522 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -23,7 +23,7 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.9.0", + "@types/node": "22.9.3", "@types/node-ipc": "9.2.3", "typescript": "4.7.4" }, diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 7b41f87ffb..54feb7df9e 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": "2024.11.3", + "version": "2024.12.0", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 3ef2dde2e8..1f181f4828 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -5,6 +5,7 @@ import { DesktopDefaultOverlayPosition, EnvironmentSelectorComponent, } from "@bitwarden/angular/auth/components/environment-selector.component"; +import { TwoFactorTimeoutComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-expired.component"; import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap"; import { authGuard, @@ -35,6 +36,7 @@ import { VaultIcon, LoginDecryptionOptionsComponent, DevicesIcon, + TwoFactorTimeoutIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -103,6 +105,22 @@ const routes: Routes = [ ], }, ), + { + path: "2fa-timeout", + component: AnonLayoutWrapperComponent, + children: [ + { + path: "", + component: TwoFactorTimeoutComponent, + }, + ], + data: { + pageIcon: TwoFactorTimeoutIcon, + pageTitle: { + key: "authenticationTimeout", + }, + } satisfies RouteDataProperties & AnonLayoutWrapperData, + }, { path: "register", component: RegisterComponent }, { path: "vault", diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index e8a9e96924..7840c1dd40 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -25,7 +25,7 @@ import { import { CollectionService } from "@bitwarden/admin-console/common"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; import { ModalService } from "@bitwarden/angular/services/modal.service"; -import { FingerprintDialogComponent } from "@bitwarden/auth/angular"; +import { FingerprintDialogComponent, LoginApprovalComponent } from "@bitwarden/auth/angular"; import { LogoutReason } from "@bitwarden/auth/common"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; @@ -67,7 +67,6 @@ import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legac import { KeyService, BiometricStateService } from "@bitwarden/key-management"; import { DeleteAccountComponent } from "../auth/delete-account.component"; -import { LoginApprovalComponent } from "../auth/login/login-approval.component"; import { MenuAccount, MenuUpdateRequest } from "../main/menu/menu.updater"; import { flagEnabled } from "../platform/flags"; import { PremiumComponent } from "../vault/app/accounts/premium.component"; diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 2a11c78a57..040102d039 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -26,6 +26,7 @@ import { } from "@bitwarden/auth/angular"; import { InternalUserDecryptionOptionsServiceAbstraction, + LoginApprovalComponentServiceAbstraction, LoginEmailService, PinServiceAbstraction, } from "@bitwarden/auth/common"; @@ -87,6 +88,7 @@ import { BiometricsService, } from "@bitwarden/key-management"; +import { DesktopLoginApprovalComponentService } from "../../auth/login/desktop-login-approval-component.service"; import { DesktopLoginComponentService } from "../../auth/login/desktop-login-component.service"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { ElectronBiometricsService } from "../../key-management/biometrics/electron-biometrics.service"; @@ -104,9 +106,10 @@ import { ElectronRendererStorageService } from "../../platform/services/electron import { I18nRendererService } from "../../platform/services/i18n.renderer.service"; import { fromIpcMessaging } from "../../platform/utils/from-ipc-messaging"; import { fromIpcSystemTheme } from "../../platform/utils/from-ipc-system-theme"; +import { BiometricMessageHandlerService } from "../../services/biometric-message-handler.service"; import { DesktopLockComponentService } from "../../services/desktop-lock-component.service"; +import { DuckDuckGoMessageHandlerService } from "../../services/duckduckgo-message-handler.service"; import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service"; -import { NativeMessageHandlerService } from "../../services/native-message-handler.service"; import { NativeMessagingService } from "../../services/native-messaging.service"; import { SearchBarService } from "../layout/search/search-bar.service"; @@ -132,6 +135,7 @@ const safeProviders: SafeProvider[] = [ deps: [], }), safeProvider(NativeMessagingService), + safeProvider(BiometricMessageHandlerService), safeProvider(SearchBarService), safeProvider(DialogService), safeProvider({ @@ -255,7 +259,7 @@ const safeProviders: SafeProvider[] = [ ], }), safeProvider({ - provide: NativeMessageHandlerService, + provide: DuckDuckGoMessageHandlerService, deps: [ StateServiceAbstraction, EncryptService, @@ -349,6 +353,11 @@ const safeProviders: SafeProvider[] = [ useClass: LoginEmailService, deps: [AccountService, AuthService, StateProvider], }), + safeProvider({ + provide: LoginApprovalComponentServiceAbstraction, + useClass: DesktopLoginApprovalComponentService, + deps: [I18nServiceAbstraction], + }), ]; @NgModule({ diff --git a/apps/desktop/src/app/tools/generator/credential-generator.component.html b/apps/desktop/src/app/tools/generator/credential-generator.component.html index 37d677472d..66b0de13df 100644 --- a/apps/desktop/src/app/tools/generator/credential-generator.component.html +++ b/apps/desktop/src/app/tools/generator/credential-generator.component.html @@ -6,6 +6,7 @@ { + let service: DesktopLoginApprovalComponentService; + let i18nService: MockProxy; + let originalIpc: any; + + beforeEach(() => { + originalIpc = (global as any).ipc; + (global as any).ipc = { + auth: { + loginRequest: jest.fn(), + }, + platform: { + isWindowVisible: jest.fn(), + }, + }; + + i18nService = mock({ + t: jest.fn(), + userSetLocale$: new Subject(), + locale$: new Subject(), + }); + + TestBed.configureTestingModule({ + providers: [ + DesktopLoginApprovalComponentService, + { provide: I18nServiceAbstraction, useValue: i18nService }, + ], + }); + + service = TestBed.inject(DesktopLoginApprovalComponentService); + }); + + afterEach(() => { + jest.clearAllMocks(); + (global as any).ipc = originalIpc; + }); + + it("is created successfully", () => { + expect(service).toBeTruthy(); + }); + + it("calls ipc.auth.loginRequest with correct parameters when window is not visible", async () => { + const title = "Log in requested"; + const email = "test@bitwarden.com"; + const message = `Confirm login attempt for ${email}`; + const closeText = "Close"; + + const loginApprovalComponent = { email } as LoginApprovalComponent; + i18nService.t.mockImplementation((key: string) => { + switch (key) { + case "logInRequested": + return title; + case "confirmLoginAtemptForMail": + return message; + case "close": + return closeText; + default: + return ""; + } + }); + + jest.spyOn(ipc.platform, "isWindowVisible").mockResolvedValue(false); + jest.spyOn(ipc.auth, "loginRequest").mockResolvedValue(); + + await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalComponent.email); + + expect(ipc.auth.loginRequest).toHaveBeenCalledWith(title, message, closeText); + }); + + it("does not call ipc.auth.loginRequest when window is visible", async () => { + const loginApprovalComponent = { email: "test@bitwarden.com" } as LoginApprovalComponent; + + jest.spyOn(ipc.platform, "isWindowVisible").mockResolvedValue(true); + jest.spyOn(ipc.auth, "loginRequest"); + + await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalComponent.email); + + expect(ipc.auth.loginRequest).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/desktop/src/auth/login/desktop-login-approval-component.service.ts b/apps/desktop/src/auth/login/desktop-login-approval-component.service.ts new file mode 100644 index 0000000000..3e658f9ba0 --- /dev/null +++ b/apps/desktop/src/auth/login/desktop-login-approval-component.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from "@angular/core"; + +import { DefaultLoginApprovalComponentService } from "@bitwarden/auth/angular"; +import { LoginApprovalComponentServiceAbstraction } from "@bitwarden/auth/common"; +import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; + +@Injectable() +export class DesktopLoginApprovalComponentService + extends DefaultLoginApprovalComponentService + implements LoginApprovalComponentServiceAbstraction +{ + constructor(private i18nService: I18nServiceAbstraction) { + super(); + } + + async showLoginRequestedAlertIfWindowNotVisible(email: string): Promise { + const isVisible = await ipc.platform.isWindowVisible(); + if (!isVisible) { + await ipc.auth.loginRequest( + this.i18nService.t("logInRequested"), + this.i18nService.t("confirmLoginAtemptForMail", email), + this.i18nService.t("close"), + ); + } + } +} diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 1a8215691f..2bf32319e2 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Wagwoordgeskiedenis" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Wis", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Daar is geen wagwoorde om te lys nie." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Ontdoen" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "'n Kennisgewing was na jou toestel gestuur." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Maak asseblief seker dat jou kluis oopgesluit is en die Vingerafdrukfrase met die ander toestel vergelyk." }, "fingerprintPhraseHeader": { "message": "Vingerafdrukfrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Aan teken met toestel moet opgestel word in die instellings van die Bitwarden toep. Nodig 'n ander opsie?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Beskou alle aantekenings opsies" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Kontroleer bekende data lekkasies vir hierdie wagwoord" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Belangrik:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aanbevole bywerking van instellings" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Toestel goedkeuring word vereis. Kies 'n goedkeuings opsie hieronder:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Onthou hierdie toestel" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Gebruiker-e-pos is vermis" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Toestel is vertroud" }, diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 769a0d04ee..56083c1f14 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -27,7 +27,7 @@ "message": "ملاحظة آمنة" }, "typeSshKey": { - "message": "SSH key" + "message": "مفتاح SSH" }, "folders": { "message": "مجلدات" @@ -36,7 +36,7 @@ "message": "مجموعات" }, "searchVault": { - "message": "البحث في الخزنة" + "message": "البحث في الخزانة" }, "addItem": { "message": "إضافة عنصر" @@ -64,7 +64,7 @@ } }, "welcomeBack": { - "message": "Welcome back" + "message": "مرحبًا بعودتك" }, "moveToOrgDesc": { "message": "اختر المؤسسة التي ترغب في نقل هذا العنصر إليها. يؤدي الانتقال إلى مؤسسة إلى نقل ملكية العنصر إلى تلك المؤسسة. لن تكون المالك المباشر لهذا العنصر بعد نقله." @@ -181,16 +181,16 @@ "message": "العنوان" }, "sshPrivateKey": { - "message": "Private key" + "message": "مفتاح خاص" }, "sshPublicKey": { - "message": "Public key" + "message": "مفتاح عام" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "بصمة الأصبع" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "نوع المفتاح" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -205,22 +205,37 @@ "message": "RSA 4096-Bit" }, "sshKeyGenerated": { - "message": "A new SSH key was generated" + "message": "أُنشئ مفتاح SSH جديد" + }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" }, "sshAgentUnlockRequired": { - "message": "Please unlock your vault to approve the SSH key request." + "message": "الرجاء فتح المخزن الخاص بك للموافقة على طلب مفتاح SSH." }, "sshAgentUnlockTimeout": { - "message": "SSH key request timed out." + "message": "انتهت مهلة طلب مفتاح SSH." }, "enableSshAgent": { - "message": "Enable SSH agent" + "message": "تمكين وكيل SSH" }, "enableSshAgentDesc": { - "message": "Enable the SSH agent to sign SSH requests right from your Bitwarden vault." + "message": "تمكين وكيل SSH لتوقيع طلبات SSH مباشرة من خزانة Bitwarden الخاص بك." }, "enableSshAgentHelp": { - "message": "The SSH agent is a service targeted at developers that allows you to sign SSH requests directly from your Bitwarden vault." + "message": "وكيل SSH هو خدمة موجهة للمطورين تسمح لك بتوقيع طلبات SSH مباشرة من مخزن Bitwarden الخاص بك." }, "premiumRequired": { "message": "مطلوب اشتراك بريميوم" @@ -308,7 +323,7 @@ "message": "توليد كلمة المرور" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "توليد عبارة المرور" }, "type": { "message": "نوع" @@ -446,13 +461,13 @@ "message": "نسخ كلمة المرور" }, "regenerateSshKey": { - "message": "Regenerate SSH key" + "message": "تجديد مفتاح SSH" }, "copySshPrivateKey": { - "message": "Copy SSH private key" + "message": "نسخ المفتاح الخاص SSH" }, "copyPassphrase": { - "message": "Copy passphrase", + "message": "نسخ عبارة المرور", "description": "Copy passphrase to clipboard" }, "copyUri": { @@ -483,11 +498,11 @@ "message": "أحرف خاصة (!@#$%^&*)" }, "include": { - "message": "Include", + "message": "تضمين", "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "تضمين أحرف كبيرة", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -495,7 +510,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "تضمين أحرف صغيرة", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -503,7 +518,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "تضمين الأرقام", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -511,7 +526,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "تضمين أحرف خاصة", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -546,11 +561,11 @@ "description": "deprecated. Use avoidAmbiguous instead." }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "تجنب الأحرف الغامضة", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "طُبقت متطلبات السياسة العامة في المؤسسة على خيارات المولدات الخاصة بك.", "description": "Indicates that a policy limits the credential generator screen." }, "searchCollection": { @@ -603,13 +618,13 @@ "message": "تم حذف المجلد" }, "loginOrCreateNewAccount": { - "message": "قم بالتسجيل أو إنشاء حساب جديد للوصول إلى خزنتك الآمنة." + "message": "قم بالتسجيل أو إنشاء حساب جديد للوصول إلى خزانتك الآمنة." }, "createAccount": { "message": "إنشاء حساب" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "جديد على Bitwarden؟" }, "setAStrongPassword": { "message": "تعيين كلمة مرور قوية" @@ -621,16 +636,16 @@ "message": "تسجيل الدخول" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "تسجيل الدخول إلى Bitwarden" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "تسجيل الدخول باستخدام مفتاح المرور" }, "loginWithDevice": { - "message": "Log in with device" + "message": "تسجيل الدخول بالجهاز" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "استخدام تسجيل دخول واحد" }, "submit": { "message": "إرسال" @@ -679,7 +694,7 @@ "message": "الانضمام إلى المؤسسة" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "انضم إلى $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -694,16 +709,16 @@ "message": "الإعدادات" }, "accountEmail": { - "message": "Account email" + "message": "البريد الالكترونى للحساب" }, "requestHint": { - "message": "Request hint" + "message": "طلب تلميح" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "طلب تلميح كلمة المرور" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "Enter your account email address and your password hint will be sent to you" + "message": "أدخل عنوان البريد الإلكتروني لحسابك وسيُرسل تلميح كلمة المرور الخاصة بك إليك" }, "passwordHint": { "message": "تلميح كلمة المرور" @@ -749,10 +764,10 @@ "message": "تم إنشاء حسابك الجديد! يمكنك الآن تسجيل الدخول." }, "newAccountCreated2": { - "message": "Your new account has been created!" + "message": "أنشئ حسابك الجديد!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "لقد قمت بتسجيل الدخول!" }, "masterPassSent": { "message": "لقد أرسلنا لك رسالة بريد إلكتروني تحتوي على تلميحات كلمة المرور الرئيسية." @@ -905,7 +920,7 @@ "message": "رابط الخادم" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "رابط خادم الاستضافة الذاتية", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -990,7 +1005,7 @@ "message": "تحميل..." }, "lockVault": { - "message": "قفل الخزنة" + "message": "قفل الخزانة" }, "passwordGenerator": { "message": "مولّد كلمة المرور" @@ -1014,7 +1029,7 @@ "message": "تابعنا" }, "syncVault": { - "message": "مزامنة الخزنة" + "message": "مزامنة الخزانة" }, "changeMasterPass": { "message": "تغيير كلمة المرور الرئيسية" @@ -1034,7 +1049,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." }, "goToWebVault": { - "message": "اذهب لخزنة الوب" + "message": "اذهب لخزانة الوب" }, "getMobileApp": { "message": "احصل على تطبيق الهاتف المحمول" @@ -1049,19 +1064,19 @@ "message": "فشلت المزامنة" }, "yourVaultIsLocked": { - "message": "خزنتك مقفلة. تحقق من هويتك للمتابعة." + "message": "خزانتك مقفلة. تحقق من هويتك للمتابعة." }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "حسابك مقفل" }, "or": { - "message": "or" + "message": "أو" }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "فتح باستخدام القياسات الحيوية" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "فتح بكلمة مرور رئيسية" }, "unlock": { "message": "فتح" @@ -1083,19 +1098,19 @@ "message": "كلمة المرور الرئيسية غير صالحة" }, "twoStepLoginConfirmation": { - "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزنة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" + "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزانة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, "twoStepLogin": { "message": "تسجيل الدخول بخطوتين" }, "vaultTimeout": { - "message": "مهلة الخزنة" + "message": "مهلة الخزانة" }, "vaultTimeout1": { - "message": "Timeout" + "message": "المهلة" }, "vaultTimeoutDesc": { - "message": "اختر وقت انتهاء مهلة خزنتك وقم بتنفيذ الإجراء المحدد." + "message": "اختر وقت انتهاء مهلة خزانتك وقم بتنفيذ الإجراء المحدد." }, "immediately": { "message": "فورًا" @@ -1299,7 +1314,7 @@ "description": "Copy credit card number" }, "copyEmail": { - "message": "Copy email" + "message": "نسخ البريد الإلكتروني" }, "copySecurityCode": { "message": "نسخ رمز الأمان", @@ -1330,10 +1345,10 @@ "message": "خيارات تسجيل الدخول بخطوتين المملوكة مثل YubiKey و Duo." }, "premiumSignUpReports": { - "message": "نظافة كلمة المرور، صحة الحساب، وتقارير خرق البيانات للحفاظ على سلامة خزنتك." + "message": "نظافة كلمة المرور، صحة الحساب، وتقارير خرق البيانات للحفاظ على سلامة خزانتك." }, "premiumSignUpTotp": { - "message": "مورد رمز التحقق (2FA) لتسجيل الدخول في خزنتك." + "message": "مورد رمز التحقق (2FA) لتسجيل الدخول في خزانتك." }, "premiumSignUpSupport": { "message": "أولوية دعم العملاء." @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "سجل كلمة المرور" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "مسح", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "لا توجد كلمات مرور للعرض." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "تراجع" }, @@ -1453,7 +1486,7 @@ } }, "copySuccessful": { - "message": "Copy Successful" + "message": "تم النسخ بنجاح" }, "errorRefreshingAccessToken": { "message": "خطأ في تحديث رمز الوصول" @@ -1552,7 +1585,7 @@ "message": "تصدير من" }, "exportVault": { - "message": "تصدير الخزنة" + "message": "تصدير الخزانة" }, "fileFormat": { "message": "صيغة الملف" @@ -1623,10 +1656,10 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "تأكيد تصدير الخزنة" + "message": "تأكيد تصدير الخزانة" }, "exportWarningDesc": { - "message": "يحتوي هذا التصدير على بيانات خزنتك بتنسيق غير مشفر. لا يجب عليك تخزين أو إرسال الملف الذي تم تصديره عبر قنوات غير آمنة (مثل البريد الإلكتروني). احذفه مباشرة بعد انتهائك من استخدامه." + "message": "يحتوي هذا التصدير على بيانات خزانتك بتنسيق غير مشفر. لا يجب عليك تخزين أو إرسال الملف الذي تم تصديره عبر قنوات غير آمنة (مثل البريد الإلكتروني). احذفه مباشرة بعد انتهائك من استخدامه." }, "encExportKeyWarningDesc": { "message": "يقوم هذا التصدير بتشفير بياناتك باستخدام مفتاح تشفير حسابك. إذا قمت بتدوير مفتاح تشفير حسابك يجب عليك التصدير مرة أخرى لأنك لن تتمكن من فك تشفير ملف التصدير هذا." @@ -1705,7 +1738,7 @@ "message": "إعدادات معرف اللمس الإضافية" }, "touchIdConsentMessage": { - "message": "فتح خزنتك" + "message": "فتح خزانتك" }, "autoPromptWindowsHello": { "message": "اسأل عن Windows Hello عند التشغيل" @@ -1723,22 +1756,22 @@ "message": "موصى به للأمان." }, "lockWithMasterPassOnRestart1": { - "message": "Lock with master password on restart" + "message": "قفل مع كلمة المرور الرئيسية عند إعادة تشغيل" }, "deleteAccount": { "message": "حذف الحساب" }, "deleteAccountDesc": { - "message": "تابع أدناه لحذف حسابك وجميع بيانات خزنتك." + "message": "تابع أدناه لحذف حسابك وجميع بيانات خزانتك." }, "deleteAccountWarning": { "message": "حذف حسابك دائم. لا يمكن التراجع عنه." }, "cannotDeleteAccount": { - "message": "Cannot delete account" + "message": "لا يمكن حذف الحساب" }, "cannotDeleteAccountDesc": { - "message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details." + "message": "لا يمكن إكمال هذا الإجراء لأن حسابك مملوك من قبل منظمة. اتصل بمسؤول مؤسستك للحصول على تفاصيل إضافية." }, "accountDeleted": { "message": "تم حذف الحساب" @@ -1793,13 +1826,13 @@ "message": "واحدة أو أكثر من سياسات المؤسسة تؤثر على إعدادات المولدات الخاصة بك." }, "vaultTimeoutAction": { - "message": "إجراء مهلة الخزنة" + "message": "إجراء مهلة الخزانة" }, "vaultTimeoutActionLockDesc": { - "message": "الخزنة المقفلة تتطلب إعادة إدخال كلمة المرور الرئيسية الخاصة بك للوصول إليها مرة أخرى." + "message": "الخزانة المقفلة تتطلب إعادة إدخال كلمة المرور الرئيسية الخاصة بك للوصول إليها مرة أخرى." }, "vaultTimeoutActionLogOutDesc": { - "message": "تسجيل الخروج من الخزنة يتطلب إعادة المصادقة للوصول إليها مرة أخرى." + "message": "تسجيل الخروج من الخزانة يتطلب إعادة المصادقة للوصول إليها مرة أخرى." }, "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "أعدنّ طريقة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك." @@ -1831,7 +1864,7 @@ "message": "حُذف نهائيًا" }, "vaultTimeoutLogOutConfirmation": { - "message": "سيؤدي تسجيل الخروج إلى إزالة جميع إمكانية الوصول إلى خزنتك ويتطلب المصادقة عبر الإنترنت بعد انتهاء المهلة. هل أنت متأكد من أنك تريد استخدام هذا الإعداد؟" + "message": "سيؤدي تسجيل الخروج إلى إزالة جميع إمكانية الوصول إلى خزانتك ويتطلب المصادقة عبر الإنترنت بعد انتهاء المهلة. هل أنت متأكد من أنك تريد استخدام هذا الإعداد؟" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "تأكيد إجراء المهلة" @@ -1851,7 +1884,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "من $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -1939,7 +1972,7 @@ "message": "السماح بتكامل المتصفح" }, "enableBrowserIntegrationDesc1": { - "message": "Used to allow biometric unlock in browsers that are not Safari." + "message": "تستخدم للسماح بفتح القفل الحيوي في المتصفحات غير Safari." }, "enableDuckDuckGoBrowserIntegration": { "message": "السماح بدمج متصفح DuckDuckGo" @@ -2044,7 +2077,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": "نص" @@ -2222,7 +2255,7 @@ "message": "تم تغيير كلمة المرور الرئيسية الخاصة بك مؤخرًا من قبل مسؤول في مؤسستك. من أجل الوصول إلى الخزانة، يجب عليك تحديثها الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. وقد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة." }, "updateWeakMasterPasswordWarning": { - "message": "كلمة المرور الرئيسية الخاصة بك لا تفي بواحدة أو أكثر من سياسات مؤسستك. من أجل الوصول إلى الخزنة، يجب عليك تحديث كلمة المرور الرئيسية الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. وقد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة." + "message": "كلمة المرور الرئيسية الخاصة بك لا تفي بواحدة أو أكثر من سياسات مؤسستك. من أجل الوصول إلى الخزانة، يجب عليك تحديث كلمة المرور الرئيسية الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. وقد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة." }, "tdeDisabledMasterPasswordRequired": { "message": "لقد قامت مؤسستك بتعطيل تشفير الجهاز الموثوق به. الرجاء تعيين كلمة مرور رئيسية للوصول إلى خزانك." @@ -2270,7 +2303,7 @@ "message": "دقائق" }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "$HOURS$ ساعة و $MINUTES$ دقيقة كحد أقصى.", "placeholders": { "hours": { "content": "$1", @@ -2283,7 +2316,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "سياسات مؤسستك تؤثر على مهلة خزنتك. الحد الأقصى المسموح به لمهلة الخزنة هو $HOURS$ ساعة(ساعات) و $MINUTES$ دقيقة(دقائق). يتم تعيين إجراء مهلة المخزن الخاص بك إلى $ACTION$.", + "message": "سياسات مؤسستك تؤثر على مهلة خزانتك. الحد الأقصى المسموح به لمهلة الخزانة هو $HOURS$ ساعة(ساعات) و $MINUTES$ دقيقة(دقائق). يتم تعيين إجراء مهلة المخزن الخاص بك إلى $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -2300,7 +2333,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "لقد حددت سياسات مؤسستك إجراء مهلة الخزنة الخاصة بك إلى $ACTION$.", + "message": "لقد حددت سياسات مؤسستك إجراء مهلة الخزانة الخاصة بك إلى $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -2309,7 +2342,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "مهلة خزنتك تتجاوز القيود التي تضعها مؤسستك." + "message": "مهلة خزانتك تتجاوز القيود التي تضعها مؤسستك." }, "inviteAccepted": { "message": "تم قبول الدعوة" @@ -2321,7 +2354,7 @@ "message": "هذه المؤسسة لديها سياسة الشركة التي ستقوم تلقائياً بتسجيلك في إعادة تعيين كلمة المرور. التسجيل سيسمح لمسؤولي المؤسسة بتغيير كلمة المرور الرئيسية الخاصة بك." }, "vaultExportDisabled": { - "message": "تصدير الخزنة معطل" + "message": "تصدير الخزانة معطل" }, "personalVaultExportPolicyInEffect": { "message": "واحدة أو أكثر من سياسات المؤسسة تمنعك من تصدير خزانتك الشخصية." @@ -2357,7 +2390,7 @@ "message": "خطأ في موصل المفتاح: تأكد من أن موصل المفتاح متاح ويعمل بشكل صحيح." }, "lockAllVaults": { - "message": "قفل جميع الخزنات" + "message": "قفل جميع الخزانات" }, "accountLimitReached": { "message": "لا يمكن تسجيل دخول أكثر من 5 حسابات في نفس الوقت." @@ -2393,7 +2426,7 @@ "message": "انتهت مهلة جلستك. يرجى العودة ومحاولة تسجيل الدخول مرة أخرى." }, "exportingPersonalVaultTitle": { - "message": "تصدير الخزنة الشخصية" + "message": "تصدير الخزانة الشخصية" }, "exportingIndividualVaultDescription": { "message": "سيتم تصدير عناصر المخزن الفردية المرتبطة بـ $EMAIL$ فقط. لن يتم إدراج عناصر مخزن المؤسسة. سيتم تصدير معلومات المنتج فقط ولن تشمل المرفقات المرتبطة بها.", @@ -2405,7 +2438,7 @@ } }, "exportingOrganizationVaultTitle": { - "message": "تصدير خزنة المؤسسة" + "message": "تصدير خزانة المؤسسة" }, "exportingOrganizationVaultDesc": { "message": "فقط مستودع المؤسسة المرتبط بـ $ORGANIZATION$ سيتم تصديره. لن يتم إدراج العناصر في المستودعات الفردية أو المنظمات الأخرى.", @@ -2420,7 +2453,7 @@ "message": "مقفل" }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "المخزن الخاص بك مقفل" }, "unlocked": { "message": "غير مقفل" @@ -2442,7 +2475,7 @@ "message": "إنشاء اسم المستخدم" }, "generateEmail": { - "message": "Generate email" + "message": "إنشاء بريد إلكتروني" }, "spinboxBoundariesHint": { "message": "Value must be between $MIN$ and $MAX$.", @@ -2507,7 +2540,7 @@ "message": "الخدمة" }, "allVaults": { - "message": "جميع الخزنات" + "message": "جميع الخزانات" }, "searchOrganization": { "message": "البحث عن مؤسسة" @@ -2522,11 +2555,11 @@ "message": "إنشاء بريد إلكتروني مستعار مع خدمة إعادة توجيه خارجية." }, "forwarderDomainName": { - "message": "Email domain", + "message": "نطاق البريد الإلكتروني", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "اختر نطاق مدعوم من قبل الخدمة المحددة", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -2654,7 +2687,7 @@ "message": "هل أنت متأكد من أنك تريد استخدام خيار \"مطلقا\"؟ إعداد خيارات القفل إلى \"مطلقا\" يخزن مفتاح تشفير المستودع الخاص بك على جهازك. إذا كنت تستخدم هذا الخيار، يجب أن تتأكد من الحفاظ على حماية جهازك بشكل صحيح." }, "vault": { - "message": "الخزنة" + "message": "الخزانة" }, "loginWithMasterPassword": { "message": "تسجيل الدخول باستخدام كلمة المرور الرئيسية" @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "تم إرسال إشعار إلى جهازك." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "الرجاء التأكد من أن المخزن الخاص بك غير مقفل وأن عبارة بصمة الإصبع تطابق الجهاز الآخر." }, "fingerprintPhraseHeader": { "message": "عبارة بصمة الإصبع" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "تسجيل الدخول باستخدام الجهاز يجب أن يتم إعداده في إعدادات تطبيق Bitwarden. هل تحتاج إلى خيار آخر؟" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "عرض جميع خيارات تسجيل الدخول" }, @@ -2817,11 +2865,14 @@ "checkForBreaches": { "message": "تحقق من خروقات البيانات المعروفة لكلمة المرور هذه" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "مهم:" }, "accessing": { - "message": "Accessing" + "message": "الوصول" }, "accessTokenUnableToBeDecrypted": { "message": "لقد قمت بتسجيل الخروج لأنه لا يمكن فك تشفير الرمز المميز الخاص بك. الرجاء تسجيل الدخول مرة أخرى لحل هذه المشكلة." @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "تحديث الإعدادات الموصى بها" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "موافقة الجهاز مطلوبة. حدد خيار الموافقة أدناه:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "تذكر هذا الجهاز" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "البريد الإلكتروني للمستخدم مفقود" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "الجهاز موثوق به" }, @@ -3138,7 +3201,7 @@ } }, "confirmVaultImport": { - "message": "تأكيد تصدير الخزنة" + "message": "تأكيد تصدير الخزانة" }, "confirmVaultImportDesc": { "message": "هذا الملف محمي بكلمة مرور. الرجاء إدخال كلمة مرور الملف لاستيراد البيانات." @@ -3294,39 +3357,39 @@ "message": "إرسال النص" }, "ssoError": { - "message": "No free ports could be found for the sso login." + "message": "لم يُعثر على أي منفذ مجاني لتسجيل الدخول." }, "authorize": { - "message": "Authorize" + "message": "السماح" }, "deny": { - "message": "Deny" + "message": "رفض" }, "sshkeyApprovalTitle": { - "message": "Confirm SSH key usage" + "message": "تأكيد استخدام مفتاح SSH" }, "sshkeyApprovalMessageInfix": { - "message": "is requesting access to" + "message": "يطلب الوصول إلى" }, "unknownApplication": { - "message": "An application" + "message": "تطبيق" }, "sshKeyPasswordUnsupported": { - "message": "Importing password protected SSH keys is not yet supported" + "message": "استيراد مفاتيح SSH المحمية بكلمة المرور غير معتمد حتى الآن" }, "invalidSshKey": { - "message": "The SSH key is invalid" + "message": "مفتاح SSH غير صالح" }, "sshKeyTypeUnsupported": { - "message": "The SSH key type is not supported" + "message": "نوع مفتاح SSH غير معتمد" }, "importSshKeyFromClipboard": { - "message": "Import key from clipboard" + "message": "استيراد المفتاح من الحافظة" }, "sshKeyPasted": { - "message": "SSH key imported successfully" + "message": "تم استيراد مفتاح SSH بنجاح" }, "fileSavedToDevice": { - "message": "File saved to device. Manage from your device downloads." + "message": "تم حفظ الملف على الجهاز. إدارة من تنزيلات جهازك." } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 92911c2fae..e49c4be041 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Yeni bir SSH açarı yaradıldı" }, + "sshKeyWrongPassword": { + "message": "Daxil etdiyiniz parol yanlışdır." + }, + "importSshKey": { + "message": "Daxilə köçür" + }, + "confirmSshKeyPassword": { + "message": "Parolu təsdiqlə" + }, + "enterSshKeyPasswordDesc": { + "message": "SSH açarının parolunu daxil edin." + }, + "enterSshKeyPassword": { + "message": "Parolu daxil et" + }, "sshAgentUnlockRequired": { "message": "SSH açar tələbini təsdiqləmək üçün seyfinizin kilidini açın." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Parol tarixçəsi" }, + "generatorHistory": { + "message": "Yaradıcı tarixçəsi" + }, + "clearGeneratorHistoryTitle": { + "message": "Yaradıcı tarixçəsini təmizlə" + }, + "cleargGeneratorHistoryDescription": { + "message": "Davam etsəniz, yaradıcı tarixçəsindəki bütün girişlər həmişəlik silinəcək. Davam etmək istədiyinizə əminsiniz?" + }, "clear": { "message": "Təmizlə", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Siyahılanacaq heç bir parol yoxdur." }, + "clearHistory": { + "message": "Tarixçəni təmizlə" + }, + "nothingToShow": { + "message": "Göstəriləcək heç nə yoxdur" + }, + "nothingGeneratedRecently": { + "message": "Təzəlikcə heç nə yaratmamısınız" + }, "undo": { "message": "Geri al" }, @@ -1738,7 +1771,7 @@ "message": "Hesab silinə bilmir" }, "cannotDeleteAccountDesc": { - "message": "Hesabınız bir təşkilata aid olduğu üçün bu əməliyyat icra edilə bilməz. Əlavə məlumat üçün təşkilatınızın administratoru ilə əlaqə saxlayın." + "message": "Hesabınız bir təşkilata aid olduğu üçün bu əməliyyat icra edilə bilməz. Əlavə məlumat üçün təşkilatınızın inzibatçısı ilə əlaqə saxlayın." }, "accountDeleted": { "message": "Hesab silindi" @@ -2219,7 +2252,7 @@ "message": "Ana parolu güncəllə" }, "updateMasterPasswordWarning": { - "message": "Ana parolunuz təzəlikcə təşkilatınızdakı bir administrator tərəfindən dəyişdirildi. Seyfə müraciət etmək üçün onu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış edəcəksiniz və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." + "message": "Ana parolunuz təzəlikcə təşkilatınızdakı bir inzibatçı tərəfindən dəyişdirildi. Seyfə müraciət etmək üçün onu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış edəcəksiniz və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." }, "updateWeakMasterPasswordWarning": { "message": "Ana parolunuz təşkilatınızdakı siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Seyfə müraciət üçün ana parolunuzu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış etmiş və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." @@ -2318,7 +2351,7 @@ "message": "Avtomatik qeydiyyat" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "Bu təşkilat, sizi \"parol sıfırlama\"da avtomatik olaraq qeydiyyata alan müəssisə siyasətinə sahibdir. Qeydiyyat, təşkilat administratorlarına ana parolunuzu dəyişdirmə icazəsi verəcək." + "message": "Bu təşkilat, sizi \"parol sıfırlama\"da avtomatik olaraq qeydiyyata alan müəssisə siyasətinə sahibdir. Qeydiyyat, təşkilat inzibatçılarına ana parolunuzu dəyişdirmə icazəsi verəcək." }, "vaultExportDisabled": { "message": "Seyf xaricə köçürmə silindi" @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Cihazınıza bir bildiriş göndərildi." }, + "aNotificationWasSentToYourDevice": { + "message": "Cihazınıza bir bildiriş göndərildi" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Hesabınızın kilidinin açıq olduğuna və barmaq izi ifadəsinin digər cihazda uyuşduğuna əmin olun" + }, + "needAnotherOptionV1": { + "message": "Başqa bir seçimə ehtiyacınız var?" + }, "fingerprintMatchInfo": { "message": "Lütfən seyfinizin kilidinin açıq olduğuna və Barmaq izi ifadəsinin digər cihazla uyuşduğuna əmin olun." }, "fingerprintPhraseHeader": { "message": "Barmaq izi ifadəsi" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Tələbiniz təsdiqləndikdə bildiriş alacaqsınız" + }, "needAnotherOption": { "message": "Cihazla giriş etmə, Bitwarden tətbiqinin ayarlarında qurulmalıdır. Başqa bir seçimə ehtiyacınız var?" }, + "viewAllLogInOptions": { + "message": "Bütün giriş seçimlərinə bax" + }, "viewAllLoginOptions": { "message": "Bütün giriş seçimlərinə bax" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Bu parol üçün bilinən məlumat pozuntularını yoxlayın" }, + "loggedInExclamation": { + "message": "Giriş edildi!" + }, "important": { "message": "Vacib:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Tövsiyə edilən Ayarlar Güncəlləməsi" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Gələcək girişləri problemsiz etmək üçün bu cihazı xatırla" + }, "deviceApprovalRequired": { "message": "Cihaz təsdiqi tələb olunur. Aşağıdan bir təsdiq variantı seçin:" }, + "deviceApprovalRequiredV2": { + "message": "Cihaz təsdiqi tələb olunur" + }, + "selectAnApprovalOptionBelow": { + "message": "Aşağıdan bir təsdiq seçimi edin" + }, "rememberThisDevice": { "message": "Bu cihazı xatırla" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "İstifadəçi e-poçtu əskikdir" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Aktiv istifadəçi e-poçtu tapılmadı. Hesabınızdan çıxış edilir." + }, "deviceTrusted": { "message": "Cihaz güvənlidir" }, diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 3efd77c7ab..7a7aa20d7c 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Гісторыя пароляў" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Ачысціць", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "У спісе адсутнічаюць паролі." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Адрабіць" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Апавяшчэнне было адпраўлена на вашу прыладу." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Пераканайцеся, што ваша сховішча разблакіравана, а фраза адбітка пальца супадае з іншай прыладай." }, "fingerprintPhraseHeader": { "message": "Фраза адбітка пальца" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Неабходна наладзіць уваход з прыладай у наладах мабільнай праграмы Bitwarden. Патрабуецца іншы варыянт?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Паглядзець усе параметры ўваходу" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Праверыць у вядомых уцечках даных для гэтага пароля" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Важна:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Рэкамендаваныя налады абнаўлення" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Патрабуецца ўхваленне прылады. Выберыце параметры ўхвалення ніжэй:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Запомніць гэту прыладу" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Адсутнічае электронная пошта карыстальніка" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Давераная прылада" }, diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 13c65d0af8..264233bbe1 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Генериран е нов SSH ключ" }, + "sshKeyWrongPassword": { + "message": "Въведената парола е неправилна." + }, + "importSshKey": { + "message": "Внасяне" + }, + "confirmSshKeyPassword": { + "message": "Потвърждаване на паролата" + }, + "enterSshKeyPasswordDesc": { + "message": "Въведете паролата за SSH-ключа." + }, + "enterSshKeyPassword": { + "message": "Въведете парола" + }, "sshAgentUnlockRequired": { "message": "Отключете трезора си, за да одобрите заявката за SSH ключ." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Хронология на паролата" }, + "generatorHistory": { + "message": "История на генерирането" + }, + "clearGeneratorHistoryTitle": { + "message": "Изчистване на историята на генериране" + }, + "cleargGeneratorHistoryDescription": { + "message": "Ако продължите, всички записи в историята на генериране ще бъдат изтрити завинаги. Наистина ли искате това?" + }, "clear": { "message": "Изчистване", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Няма пароли за показване." }, + "clearHistory": { + "message": "Изчистване на историята" + }, + "nothingToShow": { + "message": "Няма нищо за показване" + }, + "nothingGeneratedRecently": { + "message": "Скоро не сте генерирали нищо" + }, "undo": { "message": "Отмяна" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Към устройството Ви е изпратено известие." }, + "aNotificationWasSentToYourDevice": { + "message": "Към устройството Ви е изпратено известие" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Уверете се, че регистрацията Ви е отключена и че уникалната фраза съвпада с другото устройство" + }, + "needAnotherOptionV1": { + "message": "Предпочитате друг вариант?" + }, "fingerprintMatchInfo": { "message": "Уверете се, че трезорът Ви е отключен и че Уникалната фраза съвпада с другото устройство." }, "fingerprintPhraseHeader": { "message": "Уникална фраза" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Ще получите уведомление когато заявката бъде одобрена" + }, "needAnotherOption": { "message": "Вписването с устройство трябва да е включено в настройките на приложението на Битуорден. Друга настройка ли търсите?" }, + "viewAllLogInOptions": { + "message": "Вижте всички възможности за вписване" + }, "viewAllLoginOptions": { "message": "Вижте всички възможности за вписване" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Проверяване в известните случаи на изтекли данни за тази парола" }, + "loggedInExclamation": { + "message": "Вписахте се!" + }, "important": { "message": "Важно:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Препоръчителна промяна на настройките" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Запомняне на това устройство, така че в бъдеще вписването да бъде по-лесно" + }, "deviceApprovalRequired": { "message": "Изисква се одобрение на устройството. Изберете начин за одобрение по-долу:" }, + "deviceApprovalRequiredV2": { + "message": "Необходимо е одобрение на устройството" + }, + "selectAnApprovalOptionBelow": { + "message": "Изберете начин за одобряване по-долу" + }, "rememberThisDevice": { "message": "Запомняне на това устройство" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Липсва е-поща на потребителя" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Не е намерена е-поща на активен потребител. Ще бъдете отписан(а)." + }, "deviceTrusted": { "message": "Устройството е доверено" }, diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index b68c2c72e1..466dcde91d 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "পাসওয়ার্ড ইতিহাস" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "পরিষ্কার", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "তালিকার জন্য কোনও পাসওয়ার্ড নেই।" }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 9e5d64b1e8..c1dd204d81 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Historija uređivanja lozinke" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Obriši", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nema lozinki za prikazati." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Poništi" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 0725ab286a..4ccfdc2f5a 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Historial de les contrasenyes" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Esborra", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "No hi ha cap contrasenya a llistar." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Desfés" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "S'ha enviat una notificació al vostre dispositiu." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Assegureu-vos que la vostra caixa forta estiga desbloquejada i que la frase d'empremta digital coincidisca amb l'altre dispositiu." }, "fingerprintPhraseHeader": { "message": "Frase d'empremta digital" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "L'inici de sessió amb el dispositiu ha d'estar activat a la configuració de l'aplicació Bitwarden. Necessiteu una altra opció?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Veure totes les opcions d'inici de sessió" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Comproveu les filtracions de dades conegudes per a aquesta contrasenya" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Actualització de configuració recomanada" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Cal l'aprovació del dispositiu. Seleccioneu una opció d'aprovació a continuació:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Recorda aquest dispositiu" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Falta el correu electrònic de l'usuari" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Dispositiu de confiança" }, diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 7aa7f04417..0e45ca58a4 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Byl vygenerován nový SSH klíč" }, + "sshKeyWrongPassword": { + "message": "Zadané heslo není správné." + }, + "importSshKey": { + "message": "Importovat" + }, + "confirmSshKeyPassword": { + "message": "Potvrdit heslo" + }, + "enterSshKeyPasswordDesc": { + "message": "Zadejte heslo pro SSH klíč." + }, + "enterSshKeyPassword": { + "message": "Zadejte heslo" + }, "sshAgentUnlockRequired": { "message": "Pro schválení požadavku na SSH klíč odemkněte Váš trezor." }, @@ -878,7 +893,7 @@ "message": "Přidejte další poskytovatele, kteří jsou lépe podporováni v různých zařízeních (například ověřovací aplikace)." }, "twoStepOptions": { - "message": "Možnosti dvoufázového přihlášení" + "message": "Volby dvoufázového přihlášení" }, "selfHostedEnvironment": { "message": "Vlastní hostované prostředí" @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Historie hesel" }, + "generatorHistory": { + "message": "Historie generátoru" + }, + "clearGeneratorHistoryTitle": { + "message": "Vymazat historii generátoru" + }, + "cleargGeneratorHistoryDescription": { + "message": "Pokud budete pokračovat, všechny položky budou trvale smazány z historie generátoru. Jste si jisti, že chcete pokračovat?" + }, "clear": { "message": "Vymazat", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nejsou k dispozici žádná hesla." }, + "clearHistory": { + "message": "Vymazat historii" + }, + "nothingToShow": { + "message": "Nic k zobrazení" + }, + "nothingGeneratedRecently": { + "message": "Nedávno jste nic nevygenerovali" + }, "undo": { "message": "Zpět" }, @@ -2020,7 +2053,7 @@ "message": "Nápověda k Vašemu heslu nemůže být stejná jako Vaše heslo." }, "personalOwnershipPolicyInEffect": { - "message": "Zásady organizace ovlivňují možnosti vlastnictví." + "message": "Zásady organizace ovlivňují volby vlastnictví." }, "personalOwnershipPolicyInEffectImports": { "message": "Zásady organizace zablokovaly importování položek do Vašeho osobního trezoru." @@ -2651,7 +2684,7 @@ "message": "K položkám v deaktivované organizaci nemáte přístup. Požádejte o pomoc vlastníka organizace." }, "neverLockWarning": { - "message": "Opravdu chcete použít volbu \"Nikdy\"? Nastavením možností uzamčení na \"Nikdy\" bude šifrovací klíč k trezoru uložen přímo ve Vašem zařízení. Pokud tuto možnost použijete, měli byste Vaše zařízení řádně zabezpečit a chránit." + "message": "Opravdu chcete použít volbu \"Nikdy\"? Nastavením volby uzamčení na \"Nikdy\" bude šifrovací klíč k trezoru uložen přímo ve Vašem zařízení. Pokud tuto možnost použijete, měli byste Vaše zařízení řádně zabezpečit a chránit." }, "vault": { "message": "Trezor" @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Na Vaše zařízení bylo odesláno oznámení." }, + "aNotificationWasSentToYourDevice": { + "message": "Na Vaše zařízení bylo odesláno oznámení" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Ujistěte se, že je Váš účet odemčen a fráze otisku prstu se shoduje s tou na druhém zařízení" + }, + "needAnotherOptionV1": { + "message": "Potřebujete další volby?" + }, "fingerprintMatchInfo": { "message": "Ujistěte se, že je Váš trezor odemčen a fráze otisku prstu se shodují s druhým zařízením." }, "fingerprintPhraseHeader": { "message": "Fráze otisku prstu" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Budete upozorněni, jakmile bude žádost schválena" + }, "needAnotherOption": { "message": "Přihlášení zařízením musí být nastaveno v aplikaci Bitwarden pro počítač. Potřebujete další volby?" }, + "viewAllLogInOptions": { + "message": "Zobrazit všechny volby přihlášení" + }, "viewAllLoginOptions": { "message": "Zobrazit všechny volby přihlášení" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Zkontrolovat heslo, zda nebylo odhaleno" }, + "loggedInExclamation": { + "message": "Přihlášeno!" + }, "important": { "message": "Důležité:" }, @@ -2847,8 +2898,17 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aktualizace doporučených nastavení" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Zapamatovat si toto zařízení pro bezproblémové budoucí přihlášení" + }, "deviceApprovalRequired": { - "message": "Vyžaduje se schválení zařízení. Vyberte možnost schválení níže:" + "message": "Vyžaduje se schválení zařízení. Vyberte volbu schválení níže:" + }, + "deviceApprovalRequiredV2": { + "message": "Vyžaduje se schválení zařízení" + }, + "selectAnApprovalOptionBelow": { + "message": "Vyberte volbu schválení níže" }, "rememberThisDevice": { "message": "Zapamatovat toto zařízení" @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Chybí e-mail uživatele" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Aktivní uživatelský e-mail nebyl nalezen. Budete odhlášeni." + }, "deviceTrusted": { "message": "Zařízení zařazeno mezi důvěryhodné" }, diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 9f523f88d6..a77e3b61ae 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 6209563dd7..d00ca886f8 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "En ny SSH-nøgle er genereret" }, + "sshKeyWrongPassword": { + "message": "Den angivne adgangskode er forkert." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Bekræft adgangskode" + }, + "enterSshKeyPasswordDesc": { + "message": "Angiv adgangskoden til SSH-nøglen." + }, + "enterSshKeyPassword": { + "message": "Angiv adgangskode" + }, "sshAgentUnlockRequired": { "message": "Oplås boksen op for at godkende SSH-nøgleanmodningen." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Adgangskodehistorik" }, + "generatorHistory": { + "message": "Generatorhistorik" + }, + "clearGeneratorHistoryTitle": { + "message": "Ryd generatorhistorik" + }, + "cleargGeneratorHistoryDescription": { + "message": "Fortsætter man, slettes alle poster permanent fra generatorhistorikken. Sikker på, at handlingen skal udføres?" + }, "clear": { "message": "Ryd", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Der er ingen kodeord at vise." }, + "clearHistory": { + "message": "Ryd historik" + }, + "nothingToShow": { + "message": "Intet at vise" + }, + "nothingGeneratedRecently": { + "message": "Intet genereret for nylig" + }, "undo": { "message": "Fortryd" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "En notifikation er sendt til din enhed." }, + "aNotificationWasSentToYourDevice": { + "message": "En notifikation er sendt til enheden" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Sørg for, at boksen er oplåst, samt at fingeraftrykssætningen matcher på den anden enhed" + }, + "needAnotherOptionV1": { + "message": "Behov for en anden mulighed?" + }, "fingerprintMatchInfo": { "message": "Sørg for, at din boks er oplåst, og at fingeraftrykssætningen matcher den anden enheds." }, "fingerprintPhraseHeader": { "message": "Fingeraftrykssætning" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Man vil få besked, når anmodningen er godkendt" + }, "needAnotherOption": { "message": "Log ind med enhed skal være opsat i indstillingerne i Bitwarden mobil-appen. Behov for en anden mulighed?" }, + "viewAllLogInOptions": { + "message": "Vis alle indlogningsmuligheder" + }, "viewAllLoginOptions": { "message": "Vis alle indlogningsmuligheder" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Tjek kendte datalæk for denne adgangskode" }, + "loggedInExclamation": { + "message": "Logget ind!" + }, "important": { "message": "Vigtigt:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Anbefalet indstillingsopdatering" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Husk denne enhed for at gøre fremtidige indlogninger gnidningsløse" + }, "deviceApprovalRequired": { "message": "Enhedsgodkendelse kræves. Vælg en godkendelsesmulighed nedenfor:" }, + "deviceApprovalRequiredV2": { + "message": "Enhedsgodkendelse kræves" + }, + "selectAnApprovalOptionBelow": { + "message": "Vælg en godkendelsesmulighed nedenfor" + }, "rememberThisDevice": { "message": "Husk denne enhed" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Brugers e-mail mangler" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Aktiv bruger e-mail ikke fundet. Man logges ud." + }, "deviceTrusted": { "message": "Enhed betroet" }, diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 89f3338d94..7b7707d33f 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Ein neuer SSH-Schlüssel wurde generiert" }, + "sshKeyWrongPassword": { + "message": "Dein eingegebenes Passwort ist falsch." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Passwort bestätigen" + }, + "enterSshKeyPasswordDesc": { + "message": "Gib das Passwort für den SSH-Schlüssel ein." + }, + "enterSshKeyPassword": { + "message": "Passwort eingeben" + }, "sshAgentUnlockRequired": { "message": "Bitte entsperre deinen Tresor, um die SSH-Schlüsselanfrage zu genehmigen." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Passwortverlauf" }, + "generatorHistory": { + "message": "Generator-Verlauf" + }, + "clearGeneratorHistoryTitle": { + "message": "Generator-Verlauf löschen" + }, + "cleargGeneratorHistoryDescription": { + "message": "Wenn du fortfährst, werden alle Einträge dauerhaft aus dem Generator-Verlauf gelöscht. Bist du sicher, dass du fortfahren möchtest?" + }, "clear": { "message": "Leeren", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Keine Einträge zum Anzeigen vorhanden." }, + "clearHistory": { + "message": "Verlauf löschen" + }, + "nothingToShow": { + "message": "Nichts anzuzeigen" + }, + "nothingGeneratedRecently": { + "message": "Du hast in letzter Zeit nichts generiert" + }, "undo": { "message": "Rückgängig" }, @@ -2445,7 +2478,7 @@ "message": "E-Mail generieren" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Wert muss zwischen $MIN$ und $MAX$ liegen.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2459,7 +2492,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Verwende $RECOMMENDED$ oder mehr Zeichen, um ein starkes Passwort zu generieren.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2469,7 +2502,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Verwende $RECOMMENDED$ oder mehr Wörter, um eine starke Passphrase zu generieren.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Eine Benachrichtigung wurde an dein Gerät gesendet." }, + "aNotificationWasSentToYourDevice": { + "message": "Eine Benachrichtigung wurde an dein Gerät gesendet" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Stelle sicher, dass dein Konto entsperrt ist und die Fingerabdruck-Phrase mit der vom anderen Gerät übereinstimmt" + }, + "needAnotherOptionV1": { + "message": "Brauchst du eine andere Option?" + }, "fingerprintMatchInfo": { "message": "Bitte stelle sicher, dass dein Tresor entsperrt ist und die Fingerabdruck-Phrase mit dem anderen Gerät übereinstimmt." }, "fingerprintPhraseHeader": { "message": "Fingerabdruck-Phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Du wirst benachrichtigt, sobald die Anfrage genehmigt wurde" + }, "needAnotherOption": { "message": "Die Anmeldung über ein Gerät muss in den Einstellungen der Bitwarden App eingerichtet werden. Benötigst du eine andere Option?" }, + "viewAllLogInOptions": { + "message": "Alle Anmeldeoptionen anzeigen" + }, "viewAllLoginOptions": { "message": "Alle Anmeldeoptionen anzeigen" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Bekannte Datendiebstähle auf dieses Passwort überprüfen" }, + "loggedInExclamation": { + "message": "Angemeldet!" + }, "important": { "message": "Wichtig:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Empfohlene Aktualisierung der Einstellungen" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Dieses Gerät merken, um zukünftige Anmeldungen reibungslos zu gestalten" + }, "deviceApprovalRequired": { "message": "Geräte-Genehmigung erforderlich. Wähle unten eine Genehmigungsoption aus:" }, + "deviceApprovalRequiredV2": { + "message": "Geräte-Genehmigung erforderlich" + }, + "selectAnApprovalOptionBelow": { + "message": "Wähle unten eine Genehmigungsoption aus" + }, "rememberThisDevice": { "message": "Dieses Gerät merken" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "E-Mail-Adresse des Benutzers fehlt" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Aktive Benutzer-E-Mail-Adresse nicht gefunden. Du wirst abgemeldet." + }, "deviceTrusted": { "message": "Gerät wird vertraut" }, diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 4a111088b6..df5f968ef5 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Ιστορικό κωδικού πρόσβασης" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Εκκαθάριση", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Δεν υπάρχουν κωδικοί στη λίστα." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Αναίρεση" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Μια ειδοποίηση έχει σταλεί στη συσκευή σας." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Παρακαλώ βεβαιωθείτε ότι το θησαυ/κιό σας είναι ξεκλείδωτο και η φράση δακτυλικών αποτυπωμάτων ταιριάζει με την άλλη συσκευή." }, "fingerprintPhraseHeader": { "message": "Φράση δακτυλικών αποτυπωμάτων" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Η σύνδεση με τη χρήση συσκευής πρέπει να οριστεί στις ρυθμίσεις της εφαρμογής Bitwarden. Χρειάζεστε κάποια άλλη επιλογή;" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Δείτε όλες τις επιλογές σύνδεσης" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Ελέγξτε γνωστές διαρροές δεδομένων για αυτόν τον κωδικό" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Σημαντικό:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Ενημέρωση Προτεινόμενων Ρυθμίσεων" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Απαιτείται έγκριση συσκευής. Επιλέξτε μια επιλογή έγκρισης παρακάτω:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Απομνημόνευση αυτής της συσκευής" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Η διεύθυνση ηλ. ταχυδρομείου του χρήστη λείπει" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Αξιόπιστη συσκευή" }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 837535ddb0..e4c235dada 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -904,6 +919,12 @@ "baseUrl": { "message": "Server URL" }, + "authenticationTimeout": { + "message": "Authentication timeout" + }, + "authenticationSessionTimedOut": { + "message": "The authentication session timed out. Please restart the login process." + }, "selfHostBaseUrl": { "message": "Self-host server URL", "description": "Label for field requesting a self-hosted integration service URL" @@ -1752,10 +1773,10 @@ "deleteAccountWarning": { "message": "Deleting your account is permanent. It cannot be undone." }, - "cannotDeleteAccount":{ + "cannotDeleteAccount": { "message": "Cannot delete account" }, - "cannotDeleteAccountDesc":{ + "cannotDeleteAccountDesc": { "message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details." }, "accountDeleted": { diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 8e53ee684f..316ea4bf08 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 3c9e11a78a..1804169218 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 9283da114f..4499b27b84 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Pasvorta historio" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Malfari" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 14b9b20f09..3db7acb80f 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Historial de contraseñas" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Limpiar", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "No hay contraseñas que listar." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Deshacer" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Se ha enviado una notificación a tu dispositivo." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Por favor, asegúrese de que su caja fuerte está desbloqueada y la frase de huella dactilar coincide con el otro dispositivo." }, "fingerprintPhraseHeader": { "message": "Frase de huella dactilar" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Iniciar sesión con el dispositivo debe estar habilitado en los ajustes de la aplicación móvil Bitwarden. ¿Necesitas otra opción?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Ver todas las opciones de inicio de sesión" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Comprobar filtración de datos conocidos para esta contraseña" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Importante:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Actualización de ajustes recomendados" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Se requiere aprobación del dispositivo. Selecciona una opción de aprobación a continuación:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Recordar este dispositivo" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Falta el correo electrónico del usuario" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Dispositivo de confianza" }, diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 3336d07642..bf3f9861f9 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Paroolide ajalugu" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Tühjenda", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Puuduvad paroolid, mida kuvada." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Võta tagasi" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Sinu seadmesse saadeti teavitus." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Veendu, et sinu hoidla on avatud ja unikaalne sõnajada ühtib teise seadmega." }, "fingerprintPhraseHeader": { "message": "Unikaalne sõnajada" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Bitwardeni rakenduse seadistuses peab olema konfigureeritud sisselogimine läbi seadme. Vajad teist valikut?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Vaata kõiki valikuid" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Otsi seda parooli teadaolevatest andmeleketest" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Tähtis:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Soovitatud Muudatus Seadetes" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Seadme kinnitamine on nõutud. Palun vali kuidas soovid seda teha:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Hoia see seade meeles" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Kasutaja email puudub" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Usaldusväärne seade" }, diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index f3be7c7e4d..0bc53106ca 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Pasahitz historia" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Ezabatu", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Ez dago erakusteko pasahitzik." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Desegin" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index d53d6b999f..9ba711ef7f 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "تاریخچه کلمه عبور" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "پاک کردن", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "هیچ کلمه عبوری برای فهرست کردن وجود ندارد." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "بازگرداندن" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "یک اعلان به دستگاه شما ارسال شده است." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "لطفاً مطمئن شوید که قفل گاوصندوق شما باز است و عبارت اثر انگشت در دستگاه دیگر مطابقت دارد." }, "fingerprintPhraseHeader": { "message": "عبارت اثر انگشت" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "ورود به سیستم با دستگاه باید در تنظیمات برنامهی Bitwarden تنظیم شود. به گزینه دیگری نیاز دارید؟" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "مشاهده همه گزینههای ورود به سیستم" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "نقض اطلاعات شناخته شده برای این کلمه عبور را بررسی کنید" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "مهم:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "بهروز رسانی تنظیمات توصیه شده" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "تأیید دستگاه لازم است. یک روش تأیید انتخاب کنید:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "این دستگاه را به خاطر بسپار" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "ایمیل کاربر وجود ندارد" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "دستگاه مورد اعتماد است" }, diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 65b5cea9df..1fb69e5a3f 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Uusi SSH-avain luotiin" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Ole hyvä ja avaa holvisi hyväksyäksesi SSH-avainpyynnön." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Salasanahistoria" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Tyhjennä", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Ei näytettäviä salasanoja." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Kumoa" }, @@ -2445,7 +2478,7 @@ "message": "Luo sähköpostiosoite" }, "spinboxBoundariesHint": { - "message": "Arvon tulee olla väliltä $MIN$—$MAX$.", + "message": "Value must be between $MIN$ and $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2459,7 +2492,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Käytä $RECOMMENDED$ tai useampaa merkkiä vahvan salasanan luomiseen.", + "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2469,7 +2502,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Käytä $RECOMMENDED$ tai useampaa sanaa vahvan salalauseen luomiseen.", + "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Laitteellesi on lähetetty ilmoitus." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Varmista, että vahvistavan laitteen holvi on avattu ja että se näyttää saman tunnistelausekkeen." }, "fingerprintPhraseHeader": { "message": "Tunnistelauseke" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Laitteella kirjautuminen on määritettävä Bitwarden-sovelluksen asetuksista. Tarvitsetko eri vaihtoehdon?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Näytä kaikki kirjautumisvaihtoehdot" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Tarkasta esiintyykö salasanaa tunnetuissa tietovuodoissa" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Tärkeää:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Suositeltava asetusmuutos" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Laitehyväksyntä vaaditaan. Valitse hyväksyntätapa alta:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Muista tämä laite" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Käyttäjän sähköpostiosoite puuttuu" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Laitteeseen luotettu" }, diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 0351c7bfed..281f58182f 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Kasaysayan ng Password" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Hilahin", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Walang mga password na ilista." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Mag-undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Naipadala na ang notification sa iyong device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Mangyaring tiyakin na ang iyong vault ay naka unlock at ang parirala ng Fingerprint ay tumutugma sa iba pang aparato." }, "fingerprintPhraseHeader": { "message": "Hulmabig ng Hilik ng Dako" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Ang pag log in gamit ang device ay dapat na naka set up sa mga setting ng Bitwarden app. Kailangan mo ng ibang opsyon?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Tingnan ang lahat ng mga pagpipilian sa pag login" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Tingnan ang kilalang breaches ng data para sa password na ito" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Mahalaga:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index ec832b9648..df5527fd9a 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Une nouvelle clé SSH a été générée" }, + "sshKeyWrongPassword": { + "message": "Le mot de passe saisi est incorrect." + }, + "importSshKey": { + "message": "Importer" + }, + "confirmSshKeyPassword": { + "message": "Confirmez le mot de passe" + }, + "enterSshKeyPasswordDesc": { + "message": "Entrez le mot de passe de la clé SSH." + }, + "enterSshKeyPassword": { + "message": "Entrez le mot de passe" + }, "sshAgentUnlockRequired": { "message": "Veuillez déverrouiller votre coffre pour approuver la demande de clé SSH." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Historique du mot de passe" }, + "generatorHistory": { + "message": "Historique du générateur" + }, + "clearGeneratorHistoryTitle": { + "message": "Effacer l'historique du générateur" + }, + "cleargGeneratorHistoryDescription": { + "message": "Si vous continuez, toutes les entrées seront définitivement supprimées de l'historique du générateur. Êtes-vous sûr de vouloir continuer ?" + }, "clear": { "message": "Effacer", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Aucun mot de passe à afficher." }, + "clearHistory": { + "message": "Effacer l'historique" + }, + "nothingToShow": { + "message": "Rien à afficher" + }, + "nothingGeneratedRecently": { + "message": "Vous n'avez rien généré récemment" + }, "undo": { "message": "Annuler" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Une notification a été envoyée à votre appareil." }, + "aNotificationWasSentToYourDevice": { + "message": "Une notification a été envoyée à votre appareil" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Assurez-vous que votre compte est déverrouillé et que la phrase d'empreinte digitale correspond à celle de l'autre appareil" + }, + "needAnotherOptionV1": { + "message": "Besoin d'une autre option ?" + }, "fingerprintMatchInfo": { "message": "Veuillez vous assurer que votre coffre est déverrouillé et que la phrase d'empreinte correspond à celle de l'autre appareil." }, "fingerprintPhraseHeader": { "message": "Phrase d'empreinte" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Vous serez notifié une fois que la demande sera approuvée" + }, "needAnotherOption": { "message": "La connexion avec l'appareil doit être configurée dans les paramètres de l'application Bitwarden. Besoin d'une autre option ?" }, + "viewAllLogInOptions": { + "message": "Afficher toutes les options de connexion" + }, "viewAllLoginOptions": { "message": "Afficher toutes les options de connexion" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Vérifier les brèches de données connues pour ce mot de passe" }, + "loggedInExclamation": { + "message": "Connecté !" + }, "important": { "message": "Important :" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Une mise à jour des paramètres est recommandée" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Mémorisez cet appareil pour faciliter les futures connexions" + }, "deviceApprovalRequired": { "message": "L'approbation de l'appareil est requise. Sélectionnez une option d'approbation ci-dessous:" }, + "deviceApprovalRequiredV2": { + "message": "Autorisation de l'appareil requise" + }, + "selectAnApprovalOptionBelow": { + "message": "Sélectionnez une option d'approbation ci-dessous" + }, "rememberThisDevice": { "message": "Se souvenir de cet appareil" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "E-mail de l'utilisateur manquant" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Courriel utilisateur actif introuvable. Déconnexion en cours." + }, "deviceTrusted": { "message": "Appareil de confiance" }, diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index c6af45e2b2..e9bebb8bfc 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 2ed90788ec..be64236849 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "נוצר מפתח SSH חדש" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "היסטוריית סיסמאות" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "נקה", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "אין סיסמאות להצגה ברשימה." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "בטל" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "התראה נשלחה למכשירך." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "וודא שהכספת שלך לא נעולה ושם המזהה שלך מתאים למכשיר השני." }, "fingerprintPhraseHeader": { "message": "שם מזהה" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "התחברות עם מכשיר צריכה להיות מוגדרת בהגדרות האפליקציה. זקוק/ה לאפשרות נוספת?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "צפייה בכל אפשרות ההתחברות" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "חשוב:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index c13ce9892a..249bc4e285 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 7a353fc44c..6589e65567 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Povijest" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Očisti", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nema lozinki na popisu." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Poništi" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Obavijest je poslana na tvoj uređaj." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Provjeri je li trezor otključan i slaže li se jedinstvena fraza s drugim uređajem." }, "fingerprintPhraseHeader": { "message": "Jedinstvena fraza" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Prijava uređajem mora biti namještena u postavka Bitwarden mobilne aplikacije. Trebaš drugu opciju?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Pogledaj sve mogućnosti prijave" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Provjeri je li lozinka ukradena prilikom krađe podataka" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Važno:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Preporučeno ažuriranje postavki" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Potrebno je odobriti uređaj. Odaberi metodu odobravanja:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Zapamti ovaj uređaj" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Nedostaje e-pošta korisnika" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Uređaj pouzdan" }, diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 09daa2862f..97c3e1eee8 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Új SSH-kulcs jött létre." }, + "sshKeyWrongPassword": { + "message": "A megadott jelszó helytelen." + }, + "importSshKey": { + "message": "Importálás" + }, + "confirmSshKeyPassword": { + "message": "Jelszó megerősítése" + }, + "enterSshKeyPasswordDesc": { + "message": "Írjuk be az SSH kulcshoz tartozó jelszót." + }, + "enterSshKeyPassword": { + "message": "Jelszó megadása" + }, "sshAgentUnlockRequired": { "message": "Az SSH-kulcskérés jóváhagyásához oldjuk fel a széfet." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Jelszó előzmények" }, + "generatorHistory": { + "message": "Generátor előzmények" + }, + "clearGeneratorHistoryTitle": { + "message": "Generátor előzmények kiürítése" + }, + "cleargGeneratorHistoryDescription": { + "message": "Ha folytatjuk, az összes bejegyzés véglegesen törlődik a generátor előzményeiből. Biztosan folytatjuk?" + }, "clear": { "message": "Kiürítés", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nincsenek listázható jelszavak." }, + "clearHistory": { + "message": "Előzmények törlése" + }, + "nothingToShow": { + "message": "Nincs megjeleníthető elem" + }, + "nothingGeneratedRecently": { + "message": "Mostanában nem lett semmi generálva." + }, "undo": { "message": "Visszavonás" }, @@ -2469,7 +2502,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " $RECOMMENDED$ vagy több szó erős jelmondat generálásához.", + "message": "Használjunk $RECOMMENDED$ vagy több szót erős jelmondat generálásához.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Egy értesítés lett elküldve az eszközre." }, + "aNotificationWasSentToYourDevice": { + "message": "Egy értesítés lett elküldve az eszközre." + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Ellenőrizzük, hogy a széf feloldásra került és az ujjlenyomat kifejezés egyezik a másik eszközön levővel." + }, + "needAnotherOptionV1": { + "message": "Másik opció szükséges?" + }, "fingerprintMatchInfo": { "message": "Ellenőrizzük, hogy a széf feloldásra került és az Ujjlenyomat kifejezés egyezik a másik eszközön levővel." }, "fingerprintPhraseHeader": { "message": "Ujjlenyomat kifejezés" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "A kérelem jóváhagyása után értesítés érkezik." + }, "needAnotherOption": { "message": "Az eszközzel történő bejelentkezést a Biwarden mobilalkalmazás beállításaiban kell beállítani. Másik opcióra van szükség?" }, + "viewAllLogInOptions": { + "message": "Összes bejelentkezési opció megtekintése" + }, "viewAllLoginOptions": { "message": "Összes bejelentkezési opció megtekintése" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Az ehhez a jelszóhoz tartozó ismert adatvédelmi incidensek ellenőrzése" }, + "loggedInExclamation": { + "message": "Megtörtént a bejelentkezés." + }, "important": { "message": "Fontos:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Ajánlott beállítások frissítése" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Emlékezés az eszközre, hogy zökkenőmentes legyen a jövőbeni bejelentkezés" + }, "deviceApprovalRequired": { "message": "Az eszköz jóváhagyása szükséges. Válasszunk egy jóváhagyási lehetőséget lentebb:" }, + "deviceApprovalRequiredV2": { + "message": "Eszköz jóváhagyás szükséges" + }, + "selectAnApprovalOptionBelow": { + "message": "Válasszunk lentebb egy jóváhagyási lehetőséget." + }, "rememberThisDevice": { "message": "Eszköz megjegyzése" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "A felhasználói email cím hiányzik." }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Az aktív felhasználói email cím nem található. Kijelentkeztetés történik." + }, "deviceTrusted": { "message": "Az eszköz megbízható." }, diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index a0d0fd34bf..b74e7b5f37 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Riwayat Kata Sandi" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Bersihkan", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Tidak ada sandi yang dapat dicantumkan." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Urungkan" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Periksa pelanggaran data yang diketahui untuk kata sandi ini" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Penting:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index cbc7f18b74..7704f760c4 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Cronologia delle password" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Cancella", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Non ci sono password da mostrare." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Annulla" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Una notifica è stata inviata al tuo dispositivo." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Assicurati che la tua cassaforte sia sbloccata e che la frase impronta corrisponda sull'altro dispositivo." }, "fingerprintPhraseHeader": { "message": "Frase impronta" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "L'accesso con dispositivo deve essere abilitato nelle impostazioni dell'app Bitwarden. Ti serve un'altra opzione?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Visualizza tutte le opzioni di accesso" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Controlla se la tua password è presente in una violazione dei dati" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Importante:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aggiornamento delle impostazioni consigliato" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Approvazione del dispositivo obbligatoria. Seleziona un'opzione di approvazione:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Ricorda questo dispositivo" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Email utente mancante" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Dispositivo fidato" }, diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index f77b44d74c..b7ed266780 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "パスワードの履歴" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "消去する", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "表示するパスワードがありません" }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "元に戻す" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "デバイスに通知を送信しました。" }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "保管庫がロックされていることと、パスフレーズが他のデバイスと一致していることを確認してください。" }, "fingerprintPhraseHeader": { "message": "パスフレーズ" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Bitwarden アプリの設定でデバイスでログインする必要があります。別のオプションが必要ですか?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "すべてのログインオプションを表示" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "このパスワードの既知のデータ流出を確認" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "重要" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "設定の更新を推奨" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "デバイスの承認が必要です。以下から承認オプションを選択してください:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "このデバイスを記憶する" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "ユーザーのメールアドレスがありません" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "信頼されたデバイス" }, diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index d8bd81f852..edeb817ac5 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "გაწმენდა", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "დაბრუნება" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index c6af45e2b2..e9bebb8bfc 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 8b8df967aa..ad36e6590f 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "ಪಾಸ್ವರ್ಡ್ ಇತಿಹಾಸ" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "ಕ್ಲಿಯರ್", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "ಪಟ್ಟಿ ಮಾಡಲು ಯಾವುದೇ ಪಾಸ್ವರ್ಡ್ಗಳು ಇಲ್ಲ." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "ರದ್ದುಮಾಡು" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 718b89a800..b071f1734b 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -27,7 +27,7 @@ "message": "보안 메모" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH 키" }, "folders": { "message": "폴더" @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "비밀번호 변경 기록" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "삭제", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "비밀번호가 없습니다." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "실행 취소" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "기기에 알림이 전송되었습니다." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "보관함이 잠금 해제되어있고 지문 구절이 다른 기기와 일치하는지 확인하세요." }, "fingerprintPhraseHeader": { "message": "지문 구절" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "기기로 로그인하려면 Bitwarden 앱 설정에서 설정해야 합니다. 다른 방식이 필요하신가요?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "모든 로그인 방식 보기" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "중요:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, @@ -2909,7 +2972,7 @@ "message": "Input is required." }, "required": { - "message": "required" + "message": "필수" }, "search": { "message": "Search" @@ -2979,7 +3042,7 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- 선택 --" }, "multiSelectPlaceholder": { "message": "-- Type to filter --" @@ -3024,14 +3087,14 @@ "message": "Alias domain" }, "importData": { - "message": "Import data", + "message": "데이터 가져오기", "description": "Used for the desktop menu item and the header of the import dialog" }, "importError": { "message": "Import error" }, "importErrorDesc": { - "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." + "message": "가져오려고 하는 데이터에 문제가 있습니다. 아래에 표시된 파일의 오류를 해결한 뒤 다시 시도해 주세요." }, "resolveTheErrorsBelowAndTryAgain": { "message": "Resolve the errors below and try again." @@ -3040,7 +3103,7 @@ "message": "Description" }, "importSuccess": { - "message": "Data successfully imported" + "message": "데이터 가져오기 성공" }, "importSuccessNumberOfItems": { "message": "A total of $AMOUNT$ items were imported.", @@ -3076,7 +3139,7 @@ "message": "Launch Duo in Browser" }, "importFormatError": { - "message": "Data is not formatted correctly. Please check your import file and try again." + "message": "데이터의 포맷이 올바르지 않습니다. 불러올 파일을 확인하고 다시 시도해 주십시오." }, "importNothingError": { "message": "Nothing was imported." @@ -3094,13 +3157,13 @@ "message": "Learn about your import options" }, "selectImportFolder": { - "message": "Select a folder" + "message": "폴더 선택" }, "selectImportCollection": { "message": "Select a collection" }, "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "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": { @@ -3141,7 +3204,7 @@ "message": "Confirm vault import" }, "confirmVaultImportDesc": { - "message": "This file is password-protected. Please enter the file password to import data." + "message": "이 파일은 암호로 보호받고 있습니다. 데이터를 가져오려면 파일 암호를 입력하세요." }, "confirmFilePassword": { "message": "Confirm file password" diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 2de29a3fca..c346dcbef6 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Slaptažodžių istorija" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Išvalyti", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nėra rodytinų slaptažodžių." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Anuliuoti" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Į jūsų įrenginį išsiųstas pranešimas." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Prašome užtikrinti jog jūsų saugykla atrakinta ir piršto antspaudo frazė sutampa su kitu įrenginiu." }, "fingerprintPhraseHeader": { "message": "Piršto antspaudo frazė" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Prisijungimas su įrenginiu turi būti nustatytas Bitwarden aplikacijos nustatymuose. Reikia kito pasirinkimo?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Peržiūrėti visas prisijungimo parinktis" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Patikrinti žinomus šio slaptažodžio duomenų pažeidimus" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Svarbu:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Rekomenduojamas nustatymų atnaujinimas" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Įrenginio patvirtinimas reikalingas. Pasirinkite patvirtinimo būdą toliau:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Prisiminti šį įrenginį" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Trūksta naudotojo el. pašto" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Patikimas įrenginys" }, diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index e37bad79f4..d8aca77502 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Tika izveidota jauna SSH atslēga" }, + "sshKeyWrongPassword": { + "message": "Ievadītā parole ir nepareiza." + }, + "importSshKey": { + "message": "Ievietot" + }, + "confirmSshKeyPassword": { + "message": "Apstiprināt paroli" + }, + "enterSshKeyPasswordDesc": { + "message": "Ievadīt SSH atslēgas paroli." + }, + "enterSshKeyPassword": { + "message": "Ievadīt paroli" + }, "sshAgentUnlockRequired": { "message": "Lūgums atslēgt savu glabātavu, lai apstiprinātu SSH atslēgas pieprasījumu." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Paroles izmaiņu vēsture" }, + "generatorHistory": { + "message": "Veidotāja vēsture" + }, + "clearGeneratorHistoryTitle": { + "message": "Iztīrīt veidotāja vēsturi" + }, + "cleargGeneratorHistoryDescription": { + "message": "Turpinot visi veidotāja vēstures ieraksti tiks neatgrieziniski izdzēsti. Vai tiešām turpināt?" + }, "clear": { "message": "Notīrīt", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nav paroļu, ko parādīt." }, + "clearHistory": { + "message": "Iztīrīt vēsturi" + }, + "nothingToShow": { + "message": "Nav nekā, ko parādīt" + }, + "nothingGeneratedRecently": { + "message": "Pēdējā laikā nav nekas izveidots" + }, "undo": { "message": "Atsaukt" }, @@ -2469,7 +2502,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Jāizmanto $RECOMMENDED$ vai vairāk vārdu, lai aizveidotu spēcīgu parles vārdkopu.", + "message": " Jāizmanto $RECOMMENDED$ vai vairāk vārdu, lai aizveidotu spēcīgu paroles vārdkopu.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Uz jūsu ierīci ir nosūtīts paziņojums." }, + "aNotificationWasSentToYourDevice": { + "message": "Uz ierīci tika nosūtīts paziņojums" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Lūgums pārliecināties, ka konts ir atslēgts un atpazīšanas vārdkopa ir tāda pati arī otrā ierīcē" + }, + "needAnotherOptionV1": { + "message": "Nepieciešama cita iespēja?" + }, "fingerprintMatchInfo": { "message": "Lūgums pārliecināties, ka glabātava ir atslēgta un atpazīšanas vārdkopa ir tāda pati arī citā ierīcē." }, "fingerprintPhraseHeader": { "message": "Atpazīšanas vārdkopa" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Tiks paziņots, tiklīdz pieprasījums būs apstiprināts" + }, "needAnotherOption": { "message": "Ir jāuzstāda pieteikšanās ar ierīci Bitwarden lietotnes iestatījumos. Nepieciešama cita iespēja?" }, + "viewAllLogInOptions": { + "message": "Skatīt visas pieteikšanās iespējas" + }, "viewAllLoginOptions": { "message": "Skatīt visas pieteikšanās iespējas" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Meklēt šo paroli zināmās datu noplūdēs" }, + "loggedInExclamation": { + "message": "Pieteicies." + }, "important": { "message": "Svarīgi:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Ieteicamie iestatījumu atjauninājumi" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Atcerēties šo ierīci, lai nākotnes pieteikšanos padarītu plūdenāku" + }, "deviceApprovalRequired": { "message": "Nepieciešams ierīces apstiprinājums. Zemāk jāatlasa apstiprinājuma iespēja:" }, + "deviceApprovalRequiredV2": { + "message": "Nepieciešama ierīces apstiprināšana" + }, + "selectAnApprovalOptionBelow": { + "message": "Zemāk jāatlasa apstiprināšnas iespēja" + }, "rememberThisDevice": { "message": "Atcerēties šo ierīci" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Trūkst lietotāja e-pasta adreses" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Aktīva lietotāja e-pasta adrese netika atrasta. Notiek atteikšanās." + }, "deviceTrusted": { "message": "Ierīce ir uzticama" }, diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 4b43a97148..f6a7e899ae 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Istorija lozinki" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Očisti", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nema lozinki za prikazivanje." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Poništi" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 49bf79d325..35df8e7461 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "പാസ്സ്വേഡ് ചരിത്രം" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "മായ്ക്കുക", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "പ്രദർശിപ്പിക്കാൻ പാസ്സ്വേഡുകൾ ഒന്നും ഇല്ല." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "തിരിച്ചാക്കുക" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index c6af45e2b2..e9bebb8bfc 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 0d05d5459a..f0d9edb57d 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 9467ffe2e2..12a1608c70 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Passordhistorikk" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Tøm", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Det er ingen passord å liste opp." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Angre" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Et varsel har blitt sendt til enheten din." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingeravtrykksfrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Vis alle påloggingsalternativer" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Viktig:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Husk denne enheten" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 283974e8f7..4d43f6ff11 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 334ac0bf5b..81aad63b2e 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Nieuwe SSH-sleutel gegenereerd" }, + "sshKeyWrongPassword": { + "message": "Het door jou ingevoerde wachtwoord is onjuist." + }, + "importSshKey": { + "message": "Importeer" + }, + "confirmSshKeyPassword": { + "message": "Wachtwoord bevestigen" + }, + "enterSshKeyPasswordDesc": { + "message": "Voer het wachtwoord voor de SSH-sleutel in." + }, + "enterSshKeyPassword": { + "message": "Wachtwoord invoeren" + }, "sshAgentUnlockRequired": { "message": "Ontgrendel je kluis voor het goedkeuren van het SSH-sleutelverzoek." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Geschiedenis" }, + "generatorHistory": { + "message": "Generatorgeschiedenis" + }, + "clearGeneratorHistoryTitle": { + "message": "Generatorgeschiedenis wissen" + }, + "cleargGeneratorHistoryDescription": { + "message": "Als je doorgaat, wis je definitief de geschiedenis van de generator. Weet je zeker dat je wilt doorgaan?" + }, "clear": { "message": "Wissen", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Er zijn geen wachtwoorden om weer te geven." }, + "clearHistory": { + "message": "Geschiedenis wissen" + }, + "nothingToShow": { + "message": "Niets weer te geven" + }, + "nothingGeneratedRecently": { + "message": "Je hebt de laatste tijd niets gegenereerd" + }, "undo": { "message": "Ongedaan maken" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Er is een melding naar je apparaat verzonden." }, + "aNotificationWasSentToYourDevice": { + "message": "Er is een melding naar je apparaat verzonden" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Zorg ervoor dat je account is ontgrendeld en dat de vingerafdrukzin overeenkomt met het andere apparaat" + }, + "needAnotherOptionV1": { + "message": "Nog een optie nodig?" + }, "fingerprintMatchInfo": { "message": "Controleer of je kluis is ontgrendeld en de vingerafdrukzin overeenkomt met het andere apparaat." }, "fingerprintPhraseHeader": { "message": "Vingerafdrukzin" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Je krijgt een melding zodra de aanvraag is goedgekeurd" + }, "needAnotherOption": { "message": "Je moet Inloggen met apparaat instellen in de instellingen van de Bitwarden-app. Behoefte aan een andere mogelijkheid?" }, + "viewAllLogInOptions": { + "message": "Alle inlogopties bekijken" + }, "viewAllLoginOptions": { "message": "Alle loginopties bekijken" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Bekende datalekken voor dit wachtwoord controleren" }, + "loggedInExclamation": { + "message": "Ingelogd!" + }, "important": { "message": "Belangrijk:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aanbevolen instellingen" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Onthoud dit apparaat om in het vervolg naadloos in te loggen" + }, "deviceApprovalRequired": { "message": "Apparaattoestemming vereist. Kies een goedkeuringsoptie hieronder:" }, + "deviceApprovalRequiredV2": { + "message": "Toestemming apparaat vereist" + }, + "selectAnApprovalOptionBelow": { + "message": "Kies hieronder een goedkeuringsoptie" + }, "rememberThisDevice": { "message": "Dit apparaat onthouden" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Gebruikerse-mailadres ontbreekt" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "E-mailadres voor actieve gebruiker niet gevonden. Je wordt uitgelogd." + }, "deviceTrusted": { "message": "Vertrouwd apparaat" }, diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index d28f43b87e..210c32b624 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Passordhistorikk" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Tøm", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Det er ingen passord å vise." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Angre" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 9e5e4305f7..eff19372cb 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 599f40a292..e69fcbd73a 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Historia hasła" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Wyczyść", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Brak haseł." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Cofnij" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Powiadomienie zostało wysłane na urządzenie." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Upewnij się, że sejf jest odblokowany, a unikalny identyfikator konta pasuje do innego urządzenia." }, "fingerprintPhraseHeader": { "message": "Unikalny identyfikator konta" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Logowanie za pomocą urządzenia musi być włączone w ustawieniach aplikacji Bitwarden. Potrzebujesz innej opcji?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Zobacz wszystkie sposoby logowania" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Sprawdź znane naruszenia ochrony danych tego hasła" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Ważne:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Aktualizacja ustawień zalecanych" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Wymagane zatwierdzenie urządzenia. Wybierz opcję zatwierdzenia poniżej:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Zapamiętaj to urządzenie" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Brak adresu e-mail użytkownika" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Zaufano urządzeniu" }, diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index da66553015..b9ec08a2de 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Uma nova chave SSH foi gerada" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Por favor, desbloqueie seu cofre para aprovar a solicitação de chave SSH." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Histórico de senhas" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Limpar", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Não existem senhas para listar." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Desfazer" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Uma notificação foi enviada para seu dispositivo." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Por favor, certifique-se de que o seu cofre esteja desbloqueado e a frase de identificação corresponda ao outro dispositivo." }, "fingerprintPhraseHeader": { "message": "Frase de identificação" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Login com dispositivo deve ser habilitado nas configurações do aplicativo móvel do Bitwarden. Necessita de outra opção?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Ver todas as opções de login" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Verificar vazamentos de dados conhecidos para esta senha" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Importante:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Atualização de configurações recomendadas" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Aprovação do dispositivo necessária. Selecione uma opção de aprovação abaixo:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Lembrar deste dispositivo" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "E-mail do usuário ausente" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Dispositivo confiável" }, diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 1979450625..9e1ccde419 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Foi gerada uma nova chave SSH" }, + "sshKeyWrongPassword": { + "message": "A palavra-passe que introduziu está incorreta." + }, + "importSshKey": { + "message": "Importar" + }, + "confirmSshKeyPassword": { + "message": "Confirmar palavra-passe" + }, + "enterSshKeyPasswordDesc": { + "message": "Introduza a palavra-passe para a chave SSH." + }, + "enterSshKeyPassword": { + "message": "Introduzir palavra-passe" + }, "sshAgentUnlockRequired": { "message": "Por favor, desbloqueie o seu cofre para aprovar o pedido de chave SSH." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Histórico de palavras-passe" }, + "generatorHistory": { + "message": "Histórico do gerador" + }, + "clearGeneratorHistoryTitle": { + "message": "Limpar o histórico do gerador" + }, + "cleargGeneratorHistoryDescription": { + "message": "Se continuar, todas as entradas serão permanentemente eliminadas do histórico do gerador. Tem a certeza de que pretende continuar?" + }, "clear": { "message": "Limpar", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Não existem palavras-passe para listar." }, + "clearHistory": { + "message": "Limpar histórico" + }, + "nothingToShow": { + "message": "Nada a mostrar" + }, + "nothingGeneratedRecently": { + "message": "Não gerou nada recentemente" + }, "undo": { "message": "Anular" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Foi enviada uma notificação para o seu dispositivo." }, + "aNotificationWasSentToYourDevice": { + "message": "Foi enviada uma notificação para o seu dispositivo" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Certifique-se de que a sua conta está desbloqueada e que a frase de impressão digital corresponde à do outro dispositivo" + }, + "needAnotherOptionV1": { + "message": "Precisa de outra opção?" + }, "fingerprintMatchInfo": { "message": "Por favor, certifique-se de que o cofre está desbloqueado e que a frase de impressão digital corresponde à do outro dispositivo." }, "fingerprintPhraseHeader": { "message": "Frase de impressão digital" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Será notificado quando o pedido for aprovado" + }, "needAnotherOption": { "message": "O início de sessão com o dispositivo deve ser ativado nas definições da aplicação Bitwarden. Precisa de outra opção?" }, + "viewAllLogInOptions": { + "message": "Ver todas as opções de início de sessão" + }, "viewAllLoginOptions": { "message": "Ver todas as opções de início de sessão" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Verificar violações de dados conhecidas para esta palavra-passe" }, + "loggedInExclamation": { + "message": "Sessão iniciada!" + }, "important": { "message": "Importante:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Atualização de definições recomendadas" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Memorizar este dispositivo para facilitar futuros inícios de sessão" + }, "deviceApprovalRequired": { "message": "É necessária a aprovação do dispositivo. Selecione uma opção de aprovação abaixo:" }, + "deviceApprovalRequiredV2": { + "message": "Aprovação do dispositivo necessária" + }, + "selectAnApprovalOptionBelow": { + "message": "Selecione uma opção de aprovação abaixo" + }, "rememberThisDevice": { "message": "Lembrar este dispositivo" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "E-mail do utilizador em falta" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "O e-mail do utilizador ativo não foi encontrado. A terminar a sessão." + }, "deviceTrusted": { "message": "Dispositivo de confiança" }, diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 913577e2bd..c1e8f81824 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Istoric parole" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Ștergere", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Nicio parolă de afișat." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Anulare" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index cb733318d6..51f6353ddd 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Был создан новый ключ SSH" }, + "sshKeyWrongPassword": { + "message": "Введенный пароль неверен." + }, + "importSshKey": { + "message": "Импорт" + }, + "confirmSshKeyPassword": { + "message": "Подтвердите пароль" + }, + "enterSshKeyPasswordDesc": { + "message": "Введите пароль для ключа SSH." + }, + "enterSshKeyPassword": { + "message": "Введите пароль" + }, "sshAgentUnlockRequired": { "message": "Пожалуйста, разблокируйте свое хранилище для подтверждения запроса ключа SSH." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "История паролей" }, + "generatorHistory": { + "message": "История генератора" + }, + "clearGeneratorHistoryTitle": { + "message": "Очистить историю генератора" + }, + "cleargGeneratorHistoryDescription": { + "message": "Если вы продолжите, все записи будут навсегда удалены из истории генератора. Вы уверены, что хотите продолжить?" + }, "clear": { "message": "Очистить", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Нет паролей для отображения." }, + "clearHistory": { + "message": "Очистить историю" + }, + "nothingToShow": { + "message": "Нечего показать" + }, + "nothingGeneratedRecently": { + "message": "Вы ничего не создавали в последнее время" + }, "undo": { "message": "Отменить" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "На ваше устройство отправлено уведомление." }, + "aNotificationWasSentToYourDevice": { + "message": "На ваше устройство было отправлено уведомление" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Убедитесь, что ваш аккаунт разблокирован и фраза отпечатка совпадает с фразой на другом устройстве" + }, + "needAnotherOptionV1": { + "message": "Нужен другой вариант?" + }, "fingerprintMatchInfo": { "message": "Убедитесь, что ваше хранилище разблокировано и фраза отпечатка совпадает на другом устройстве." }, "fingerprintPhraseHeader": { "message": "Фраза отпечатка" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Вы получите уведомление, когда запрос будет одобрен" + }, "needAnotherOption": { "message": "Вход с устройства должен быть настроен в настройках мобильного приложения Bitwarden. Нужен другой вариант?" }, + "viewAllLogInOptions": { + "message": "Посмотреть все варианты авторизации" + }, "viewAllLoginOptions": { "message": "Посмотреть все варианты авторизации" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Проверять известные случаи утечки данных для этого пароля" }, + "loggedInExclamation": { + "message": "Выполнен вход!" + }, "important": { "message": "Важно:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Рекомендуемое обновление настроек" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Запомнить это устройство, чтобы в будущем авторизовываться быстрее" + }, "deviceApprovalRequired": { "message": "Требуется одобрение устройства. Выберите вариант ниже:" }, + "deviceApprovalRequiredV2": { + "message": "Требуется подтверждение устройства" + }, + "selectAnApprovalOptionBelow": { + "message": "Выберите вариант подтверждения ниже" + }, "rememberThisDevice": { "message": "Запомнить это устройство" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Отсутствует email пользователя" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Email активного пользователя не найден. Разлогиниваем." + }, "deviceTrusted": { "message": "Доверенное устройство" }, diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index a3627ed3c3..9d5047f92b 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index e86b85b028..c7ac8a5e2c 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Bol vygenerovaný nový kľúč SSH" }, + "sshKeyWrongPassword": { + "message": "Zadané heslo je nesprávne." + }, + "importSshKey": { + "message": "Importovať" + }, + "confirmSshKeyPassword": { + "message": "Potvrdiť heslo" + }, + "enterSshKeyPasswordDesc": { + "message": "Zadajte heslo pre kľúč SSH." + }, + "enterSshKeyPassword": { + "message": "Zadať heslo" + }, "sshAgentUnlockRequired": { "message": "Prosím, odomknite svoj trezor, aby ste mohli schváliť žiadosť o kľúč SSH." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "História hesla" }, + "generatorHistory": { + "message": "História generátora" + }, + "clearGeneratorHistoryTitle": { + "message": "Vymazať históriu generátora" + }, + "cleargGeneratorHistoryDescription": { + "message": "Ak budete pokračovať, všetky položky z histórie generátora budu natrvalo vymazané. Naozaj chcete pokračovať?" + }, "clear": { "message": "Vyčistiť", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Neboli nájdené žiadne heslá." }, + "clearHistory": { + "message": "Vymazať históriu" + }, + "nothingToShow": { + "message": "Nie je čo zobraziť" + }, + "nothingGeneratedRecently": { + "message": "V poslednej dobe ste nič negenerovali" + }, "undo": { "message": "Späť" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Do vášho zariadenia bolo odoslané upozornenie." }, + "aNotificationWasSentToYourDevice": { + "message": "Do vášho zariadenia bolo odoslané upozornenie" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Uistite sa, že je váš účet odomknutý a fráza odtlačku prsta sa zhoduje s frázou na druhom zariadení" + }, + "needAnotherOptionV1": { + "message": "Potrebujete inú možnosť?" + }, "fingerprintMatchInfo": { "message": "Skontrolujte, či je trezor odomknutý a či sa fráza odtlačku prsta zhoduje s druhým zariadením." }, "fingerprintPhraseHeader": { "message": "Fráza odtlačku prsta" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "Po schválení žiadosti budete informovaní" + }, "needAnotherOption": { "message": "Prihlásenie pomocou zariadenia musí byť nastavené v nastaveniach aplikácie Bitwarden. Potrebujete inú možnosť?" }, + "viewAllLogInOptions": { + "message": "Zobraziť všetky možnosti prihlásenia" + }, "viewAllLoginOptions": { "message": "Zobraziť všetky možnosti prihlásenia" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Skontrolovať známe úniky údajov pre toto heslo" }, + "loggedInExclamation": { + "message": "Prihlásený!" + }, "important": { "message": "Dôležité:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Odporúčaná aktualizácia nastavenia" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Zapamätať si toto zariadenie, pre budúce bezproblémové prihlásenie" + }, "deviceApprovalRequired": { "message": "Vyžaduje sa schválenie zariadenia. Vyberte možnosť schválenia nižšie:" }, + "deviceApprovalRequiredV2": { + "message": "Vyžaduje sa schválenie zariadenia" + }, + "selectAnApprovalOptionBelow": { + "message": "Vyberte možnosť schválenia nižšie" + }, "rememberThisDevice": { "message": "Zapamätať si toto zariadenie" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Chýba e-mail používateľa" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "E-mail aktívneho používateľa sa nenašiel. Odhlasuje sa." + }, "deviceTrusted": { "message": "Dôveryhodné zariadenie" }, diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index ce051d6aad..9603136c75 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Zgodovina gesla" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Počisti", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Ni gesel za na seznam." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Razveljavi" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 1e492a658c..eca561f2fd 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Историја Лозинке" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Очисти", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Нема лозинки за приказивање." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Опозови" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Обавештење је послато на ваш уређај." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Уверите се да је ваш сеф откључан и да се фраза отиска прста подудара на другом уређају." }, "fingerprintPhraseHeader": { "message": "Сигурносна фраза сефа" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Пријава помоћу уређаја мора бити подешена у подешавањима Bitwarden апликације. Потребна је друга опција?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Погредајте све опције пријављивања" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Проверите познате упада података за ову лозинку" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Важно:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Препоручено ажурирање поставки" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Потребно је одобрење уређаја. Изаберите опцију одобрења испод:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Запамти овај уређај" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Недостаје имејл корисника" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Уређај поуздан" }, diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 99eb834732..624a6c666b 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "En ny SSH-nyckel har genererats" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Lösenordshistorik" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Rensa", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Det finns inga lösenord att visa." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Ångra" }, @@ -2445,7 +2478,7 @@ "message": "Generate email" }, "spinboxBoundariesHint": { - "message": "Värde måste vara mellan $MIN$ och $MAX$.", + "message": "Value must be between $MIN$ and $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2459,7 +2492,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Använd minst $RECOMMENDED$ tecken för att generera ett starkt lösenord.", + "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2469,7 +2502,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Använd minst $RECOMMENDED$ ord för att generera en stark lösenfras.", + "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "En avisering har skickats till din enhet." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Se till att ditt valv är upplåst och att fingeravtrycksfrasen matchar på den andra enheten." }, "fingerprintPhraseHeader": { "message": "Fingeravtrycksfras" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "\"Logga in med enhet\" måste ställas in i inställningarna i Bitwardens app. Behöver du ett annat alternativ?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Visa alla inloggningsalternativ" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Kontrollera kända dataintrång för detta lösenord" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Viktigt:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Rekommenderade inställningsuppdateringar" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Godkännande av enhet krävs. Välj ett godkännandealternativ nedan:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Kom ihåg denna enhet" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Användarens e-postadress saknas" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Enhet betrodd" }, diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index c6af45e2b2..e9bebb8bfc 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 6052ed8ba6..8dd9e8c4f4 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "ประวัติของรหัสผ่าน" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Clear", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Undo" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { "message": "Fingerprint phrase" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "View all login options" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Check known data breaches for this password" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Important:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "User email missing" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Device trusted" }, diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index ded4216a01..bfda568235 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "Girdiğiniz parola yanlış." + }, + "importSshKey": { + "message": "İçe aktar" + }, + "confirmSshKeyPassword": { + "message": "Parolayı onaylayın" + }, + "enterSshKeyPasswordDesc": { + "message": "SSH anahtarının parolasını girin." + }, + "enterSshKeyPassword": { + "message": "Parolayı girin" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Parola geçmişi" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Temizle", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Listelenecek şifre yok." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Geri al" }, @@ -2445,7 +2478,7 @@ "message": "E-posta oluştur" }, "spinboxBoundariesHint": { - "message": "Değer $MIN$ ile $MAX$ arasında olmalıdır.", + "message": "Value must be between $MIN$ and $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Cihazınıza bir bildirim gönderildi." }, + "aNotificationWasSentToYourDevice": { + "message": "Cihazınıza bir bildirim gönderildi" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Lütfen hesabınızın kilidinin açık olduğundan ve parmak izi ifadesinin diğer cihazla eşleştiğinden emin olun" + }, + "needAnotherOptionV1": { + "message": "Başka bir seçeneğe mi ihtiyacınız var?" + }, "fingerprintMatchInfo": { "message": "Lütfen kasanızın kilidinin açık olduğundan ve parmak izi ifadesinin diğer cihazla eşleştiğinden emin olun." }, "fingerprintPhraseHeader": { "message": "Parmak izi ifadesi" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "İsteğiniz onaylanınca size haber vereceğiz" + }, "needAnotherOption": { "message": "Cihazla girişi Bitwarden mobil uygulamasının ayarlarından etkinleştirmelisiniz. Başka bir seçeneğe mi ihtiyacınız var?" }, + "viewAllLogInOptions": { + "message": "Tüm giriş seçeneklerini gör" + }, "viewAllLoginOptions": { "message": "Tüm giriş seçeneklerini gör" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Bilinen veri ihlallerinde bu parolayı kontrol et" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Önemli:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Önerilen Ayarlar Güncellemesi" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Cihaz onayı gerekiyor. Lütfen onay yönteminizi seçin:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Bu cihazı hatırla" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Kullanıcının e-postası eksik" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Cihaza güvenildi" }, diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 91c88e3c82..733994d03b 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "Згенеровано новий ключ SSH" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Розблокуйте своє сховище, щоб затвердити запит ключа SSH." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Історія паролів" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Стерти", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Немає паролів." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Повернути" }, @@ -2445,7 +2478,7 @@ "message": "Генерувати е-пошту" }, "spinboxBoundariesHint": { - "message": "Значення має бути між $MIN$ та $MAX$.", + "message": "Value must be between $MIN$ and $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2459,7 +2492,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Використовуйте $RECOMMENDED$ або більше символів, щоб згенерувати надійний пароль.", + "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2469,7 +2502,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Використовуйте $RECOMMENDED$ або більше слів, щоб згенерувати надійну парольну фразу.", + "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Сповіщення було надіслано на ваш пристрій." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Переконайтеся, що ваше сховище розблоковане, а фраза відбитка збігається з іншим пристроєм." }, "fingerprintPhraseHeader": { "message": "Фраза відбитка" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Потрібно увімкнути схвалення запитів на вхід у налаштуваннях програми Bitwarden. Потрібен інший варіант?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Переглянути всі варіанти входу" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Перевірити відомі витоки даних для цього пароля" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Важливо:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Оновлення рекомендованих налаштувань" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Необхідне підтвердження пристрою. Виберіть варіант підтвердження нижче:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Запам'ятати цей пристрій" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Немає адреси електронної пошти" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Довірений пристрій" }, diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 63225a25f0..2da1b5e409 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "Lịch sử mật khẩu" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "Xoá", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "Chưa có mật khẩu." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "Hoàn tác" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "Một thông báo đã được gửi đến thiết bị của bạn." }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "Vui lòng đảm bảo rằng bạn đã mở khoá kho và cụm vân tay khớp trên thiết bị khác." }, "fingerprintPhraseHeader": { "message": "Cụm vân tay" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "Đăng nhập bằng thiết bị phải được thiết lập trong cài đặt của ứng dụng Bitwarden. Dùng cách khác?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "Xem tất cả tùy chọn đăng nhập" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "Kiểm tra mật khẩu có lộ trong các vụ rò rỉ dữ liệu hay không" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "Quan trọng:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "Cập nhật cài đặt được đề xuất" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "Yêu cầu phê duyệt thiết bị. Chọn một tuỳ chọn phê duyệt bên dưới:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Lưu thiết bị này" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "Thiếu email người dùng" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "Thiết bị tin cậy" }, diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 65c52dc345..12ee33649a 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "生成了新的 SSH 密钥" }, + "sshKeyWrongPassword": { + "message": "您输入的密码不正确。" + }, + "importSshKey": { + "message": "导入" + }, + "confirmSshKeyPassword": { + "message": "确认密码" + }, + "enterSshKeyPasswordDesc": { + "message": "输入 SSH 密钥的密码。" + }, + "enterSshKeyPassword": { + "message": "输入密码" + }, "sshAgentUnlockRequired": { "message": "请解锁您的密码库以批准 SSH 密钥请求。" }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "密码历史记录" }, + "generatorHistory": { + "message": "生成器历史记录" + }, + "clearGeneratorHistoryTitle": { + "message": "清除生成器历史记录" + }, + "cleargGeneratorHistoryDescription": { + "message": "若继续,所有条目将从生成器历史记录中永久删除。确定要继续吗?" + }, "clear": { "message": "清除", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "没有可列出的密码。" }, + "clearHistory": { + "message": "清除历史记录" + }, + "nothingToShow": { + "message": "没有可显示的内容" + }, + "nothingGeneratedRecently": { + "message": "您最近没有生成任何内容" + }, "undo": { "message": "撤销" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "通知已发送到您的设备。" }, + "aNotificationWasSentToYourDevice": { + "message": "通知已发送到您的设备" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "确保您的账户已解锁,并且指纹短语与其他设备上的相匹配。" + }, + "needAnotherOptionV1": { + "message": "需要其他选项吗?" + }, "fingerprintMatchInfo": { "message": "请确保您的密码库已解锁,并且指纹短语与其他设备上的相匹配。" }, "fingerprintPhraseHeader": { "message": "指纹短语" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "请求获得批准后,您将收到通知" + }, "needAnotherOption": { "message": "必须在 Bitwarden App 的设置中启用设备登录。需要其他登录选项吗?" }, + "viewAllLogInOptions": { + "message": "查看所有登录选项" + }, "viewAllLoginOptions": { "message": "查看所有登录选项" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "检查已知的数据泄露是否包含此密码" }, + "loggedInExclamation": { + "message": "已登录!" + }, "important": { "message": "重要事项:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "推荐的设置更新" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "记住此设备以便将来无缝登录" + }, "deviceApprovalRequired": { "message": "需要设备批准。请在下面选择一个批准选项:" }, + "deviceApprovalRequiredV2": { + "message": "需要设备批准" + }, + "selectAnApprovalOptionBelow": { + "message": "在下方选择一个批准选项" + }, "rememberThisDevice": { "message": "记住此设备" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "缺少用户电子邮件" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "未找到活动的用户电子邮件。您将被注销。" + }, "deviceTrusted": { "message": "设备已信任" }, diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 0137487a19..8052da0887 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -207,6 +207,21 @@ "sshKeyGenerated": { "message": "A new SSH key was generated" }, + "sshKeyWrongPassword": { + "message": "The password you entered is incorrect." + }, + "importSshKey": { + "message": "Import" + }, + "confirmSshKeyPassword": { + "message": "Confirm password" + }, + "enterSshKeyPasswordDesc": { + "message": "Enter the password for the SSH key." + }, + "enterSshKeyPassword": { + "message": "Enter password" + }, "sshAgentUnlockRequired": { "message": "Please unlock your vault to approve the SSH key request." }, @@ -1371,6 +1386,15 @@ "passwordHistory": { "message": "密碼歷史記錄" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "clear": { "message": "清除", "description": "To clear something out. example: To clear browser history." @@ -1378,6 +1402,15 @@ "noPasswordsInList": { "message": "沒有可列出的密碼。" }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "undo": { "message": "復原" }, @@ -2689,15 +2722,30 @@ "notificationSentDevice": { "message": "已傳送通知至您的裝置。" }, + "aNotificationWasSentToYourDevice": { + "message": "A notification was sent to your device" + }, + "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { + "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + }, + "needAnotherOptionV1": { + "message": "Need another option?" + }, "fingerprintMatchInfo": { "message": "請確保您的密碼庫已解鎖,並且指紋短語與其他裝置的一致。" }, "fingerprintPhraseHeader": { "message": "指紋短語" }, + "youWillBeNotifiedOnceTheRequestIsApproved": { + "message": "You will be notified once the request is approved" + }, "needAnotherOption": { "message": "必須先在 Bitwarden 應用程式設定中開啟,才可以使用裝置登入。要改用其他選項嗎?" }, + "viewAllLogInOptions": { + "message": "View all log in options" + }, "viewAllLoginOptions": { "message": "檢視所有登入選項" }, @@ -2817,6 +2865,9 @@ "checkForBreaches": { "message": "檢查外洩的密碼資料庫中是否包含此密碼" }, + "loggedInExclamation": { + "message": "Logged in!" + }, "important": { "message": "重要:" }, @@ -2847,9 +2898,18 @@ "windowsBiometricUpdateWarningTitle": { "message": "建議設定更新" }, + "rememberThisDeviceToMakeFutureLoginsSeamless": { + "message": "Remember this device to make future logins seamless" + }, "deviceApprovalRequired": { "message": "裝置需要取得核准。請在下面選擇一個核准選項:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "記住這個裝置" }, @@ -2902,6 +2962,9 @@ "userEmailMissing": { "message": "缺少使用者電子郵件地址" }, + "activeUserEmailNotFoundLoggingYouOut": { + "message": "Active user email not found. Logging you out." + }, "deviceTrusted": { "message": "裝置已信任" }, diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 83a45e331d..085181e34b 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2024.11.3", + "version": "2024.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2024.11.3", + "version": "2024.12.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi", diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 394c4d72f0..0f82609b76 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": "2024.11.3", + "version": "2024.12.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/platform/main/main-ssh-agent.service.ts b/apps/desktop/src/platform/main/main-ssh-agent.service.ts index 60487aae4d..5284029c69 100644 --- a/apps/desktop/src/platform/main/main-ssh-agent.service.ts +++ b/apps/desktop/src/platform/main/main-ssh-agent.service.ts @@ -27,7 +27,7 @@ export class MainSshAgentService { init() { // handle sign request passing to UI sshagent - .serve(async (err: Error, cipherId: string) => { + .serve(async (err: Error, cipherId: string, isListRequest: boolean) => { // clear all old (> SIGN_TIMEOUT) requests this.requestResponses = this.requestResponses.filter( (response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT), @@ -37,6 +37,7 @@ export class MainSshAgentService { const id_for_this_request = this.request_id; this.messagingService.send("sshagent.signrequest", { cipherId, + isListRequest, requestId: id_for_this_request, }); @@ -111,5 +112,11 @@ export class MainSshAgentService { sshagent.lock(this.agentState); } }); + + ipcMain.handle("sshagent.clearkeys", async (event: any) => { + if (this.agentState != null) { + sshagent.clearKeys(this.agentState); + } + }); } } diff --git a/apps/desktop/src/platform/preload.ts b/apps/desktop/src/platform/preload.ts index 171e83bbef..519ed68432 100644 --- a/apps/desktop/src/platform/preload.ts +++ b/apps/desktop/src/platform/preload.ts @@ -56,6 +56,9 @@ const sshAgent = { lock: async () => { return await ipcRenderer.invoke("sshagent.lock"); }, + clearKeys: async () => { + return await ipcRenderer.invoke("sshagent.clearkeys"); + }, importKey: async (key: string, password: string): Promise => { const res = await ipcRenderer.invoke("sshagent.importkey", { privateKey: key, diff --git a/apps/desktop/src/platform/services/ssh-agent.service.ts b/apps/desktop/src/platform/services/ssh-agent.service.ts index fdd86788ed..969300fcc5 100644 --- a/apps/desktop/src/platform/services/ssh-agent.service.ts +++ b/apps/desktop/src/platform/services/ssh-agent.service.ts @@ -5,9 +5,11 @@ import { concatMap, EMPTY, filter, + firstValueFrom, from, map, of, + skip, Subject, switchMap, takeUntil, @@ -17,6 +19,7 @@ import { withLatestFrom, } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -52,6 +55,7 @@ export class SshAgentService implements OnDestroy { private i18nService: I18nService, private desktopSettingsService: DesktopSettingsService, private configService: ConfigService, + private accountService: AccountService, ) {} async init() { @@ -112,19 +116,34 @@ export class SshAgentService implements OnDestroy { ), ), // This concatMap handles showing the dialog to approve the request. - concatMap(([message, decryptedCiphers]) => { + concatMap(async ([message, ciphers]) => { const cipherId = message.cipherId as string; + const isListRequest = message.isListRequest as boolean; const requestId = message.requestId as number; - if (decryptedCiphers === undefined) { - return of(false).pipe( - switchMap((result) => - ipc.platform.sshAgent.signRequestResponse(requestId, Boolean(result)), - ), + if (isListRequest) { + const sshCiphers = ciphers.filter( + (cipher) => cipher.type === CipherType.SshKey && !cipher.isDeleted, ); + const keys = sshCiphers.map((cipher) => { + return { + name: cipher.name, + privateKey: cipher.sshKey.privateKey, + cipherId: cipher.id, + }; + }); + await ipc.platform.sshAgent.setKeys(keys); + await ipc.platform.sshAgent.signRequestResponse(requestId, true); + return; } - const cipher = decryptedCiphers.find((cipher) => cipher.id == cipherId); + if (ciphers === undefined) { + ipc.platform.sshAgent + .signRequestResponse(requestId, false) + .catch((e) => this.logService.error("Failed to respond to SSH request", e)); + } + + const cipher = ciphers.find((cipher) => cipher.id == cipherId); ipc.platform.focusWindow(); const dialogRef = ApproveSshRequestComponent.open( @@ -133,16 +152,34 @@ export class SshAgentService implements OnDestroy { this.i18nService.t("unknownApplication"), ); - return dialogRef.closed.pipe( - switchMap((result) => { - return ipc.platform.sshAgent.signRequestResponse(requestId, Boolean(result)); - }), - ); + const result = await firstValueFrom(dialogRef.closed); + return ipc.platform.sshAgent.signRequestResponse(requestId, result); }), takeUntil(this.destroy$), ) .subscribe(); + this.accountService.activeAccount$.pipe(skip(1), takeUntil(this.destroy$)).subscribe({ + next: (account) => { + this.logService.info("Active account changed, clearing SSH keys"); + ipc.platform.sshAgent + .clearKeys() + .catch((e) => this.logService.error("Failed to clear SSH keys", e)); + }, + error: (e: unknown) => { + this.logService.error("Error in active account observable", e); + ipc.platform.sshAgent + .clearKeys() + .catch((e) => this.logService.error("Failed to clear SSH keys", e)); + }, + complete: () => { + this.logService.info("Active account observable completed, clearing SSH keys"); + ipc.platform.sshAgent + .clearKeys() + .catch((e) => this.logService.error("Failed to clear SSH keys", e)); + }, + }); + combineLatest([ timer(0, this.SSH_REFRESH_INTERVAL), this.desktopSettingsService.sshAgentEnabled$, @@ -150,7 +187,7 @@ export class SshAgentService implements OnDestroy { .pipe( concatMap(async ([, enabled]) => { if (!enabled) { - await ipc.platform.sshAgent.setKeys([]); + await ipc.platform.sshAgent.clearKeys(); return; } diff --git a/apps/desktop/src/services/biometric-message-handler.service.ts b/apps/desktop/src/services/biometric-message-handler.service.ts new file mode 100644 index 0000000000..8e5a52aba8 --- /dev/null +++ b/apps/desktop/src/services/biometric-message-handler.service.ts @@ -0,0 +1,238 @@ +import { Injectable, NgZone } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +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 { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; +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 { UserId } from "@bitwarden/common/types/guid"; +import { DialogService } from "@bitwarden/components"; +import { BiometricStateService, BiometricsService, KeyService } from "@bitwarden/key-management"; + +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 { DesktopSettingsService } from "../platform/services/desktop-settings.service"; + +const MessageValidTimeout = 10 * 1000; +const HashAlgorithmForAsymmetricEncryption = "sha1"; + +@Injectable() +export class BiometricMessageHandlerService { + constructor( + private cryptoFunctionService: CryptoFunctionService, + private keyService: KeyService, + private encryptService: EncryptService, + private logService: LogService, + private messagingService: MessagingService, + private desktopSettingService: DesktopSettingsService, + private biometricStateService: BiometricStateService, + private biometricsService: BiometricsService, + private dialogService: DialogService, + private accountService: AccountService, + private authService: AuthService, + private ngZone: NgZone, + ) {} + + async handleMessage(msg: LegacyMessageWrapper) { + const { appId, message: rawMessage } = msg as LegacyMessageWrapper; + + // Request to setup secure encryption + if ("command" in rawMessage && rawMessage.command === "setupEncryption") { + const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey); + + // Validate the UserId to ensure we are logged into the same account. + const accounts = await firstValueFrom(this.accountService.accounts$); + const userIds = Object.keys(accounts); + if (!userIds.includes(rawMessage.userId)) { + ipc.platform.nativeMessaging.sendMessage({ + command: "wrongUserId", + appId: appId, + }); + return; + } + + if (await firstValueFrom(this.desktopSettingService.browserIntegrationFingerprintEnabled$)) { + ipc.platform.nativeMessaging.sendMessage({ + command: "verifyFingerprint", + appId: appId, + }); + + const fingerprint = await this.keyService.getFingerprint( + rawMessage.userId, + remotePublicKey, + ); + + this.messagingService.send("setFocus"); + + const dialogRef = this.ngZone.run(() => + BrowserSyncVerificationDialogComponent.open(this.dialogService, { fingerprint }), + ); + + const browserSyncVerified = await firstValueFrom(dialogRef.closed); + + if (browserSyncVerified !== true) { + return; + } + } + + await this.secureCommunication(remotePublicKey, appId); + return; + } + + if ((await ipc.platform.ephemeralStore.getEphemeralValue(appId)) == null) { + ipc.platform.nativeMessaging.sendMessage({ + command: "invalidateEncryption", + appId: appId, + }); + return; + } + + const message: LegacyMessage = JSON.parse( + await this.encryptService.decryptToUtf8( + rawMessage as EncString, + SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)), + ), + ); + + // Shared secret is invalidated, force re-authentication + if (message == null) { + ipc.platform.nativeMessaging.sendMessage({ + command: "invalidateEncryption", + appId: appId, + }); + return; + } + + if (Math.abs(message.timestamp - Date.now()) > MessageValidTimeout) { + this.logService.error("NativeMessage is to old, ignoring."); + return; + } + + switch (message.command) { + case "biometricUnlock": { + const isTemporarilyDisabled = + (await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId)) && + !(await this.biometricsService.supportsBiometric()); + if (isTemporarilyDisabled) { + return this.send({ command: "biometricUnlock", response: "not available" }, appId); + } + + if (!(await this.biometricsService.supportsBiometric())) { + return this.send({ command: "biometricUnlock", response: "not supported" }, appId); + } + + const userId = + (message.userId as UserId) ?? + (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))); + + if (userId == null) { + return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId); + } + + const biometricUnlockPromise = + message.userId == null + ? firstValueFrom(this.biometricStateService.biometricUnlockEnabled$) + : this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId); + if (!(await biometricUnlockPromise)) { + await this.send({ command: "biometricUnlock", response: "not enabled" }, appId); + + return this.ngZone.run(() => + this.dialogService.openSimpleDialog({ + type: "warning", + title: { key: "biometricsNotEnabledTitle" }, + content: { key: "biometricsNotEnabledDesc" }, + cancelButtonText: null, + acceptButtonText: { key: "cancel" }, + }), + ); + } + + try { + const userKey = await this.keyService.getUserKeyFromStorage( + KeySuffixOptions.Biometric, + message.userId, + ); + + if (userKey != null) { + await this.send( + { + command: "biometricUnlock", + response: "unlocked", + userKeyB64: userKey.keyB64, + }, + appId, + ); + + const currentlyActiveAccountId = ( + await firstValueFrom(this.accountService.activeAccount$) + ).id; + const isCurrentlyActiveAccountUnlocked = + (await this.authService.getAuthStatus(userId)) == AuthenticationStatus.Unlocked; + + // prevent proc reloading an active account, when it is the same as the browser + if (currentlyActiveAccountId != message.userId || !isCurrentlyActiveAccountUnlocked) { + await ipc.platform.reloadProcess(); + } + } else { + await this.send({ command: "biometricUnlock", response: "canceled" }, appId); + } + } catch (e) { + await this.send({ command: "biometricUnlock", response: "canceled" }, appId); + } + + break; + } + case "biometricUnlockAvailable": { + const isAvailable = await this.biometricsService.supportsBiometric(); + return this.send( + { + command: "biometricUnlockAvailable", + response: isAvailable ? "available" : "not available", + }, + appId, + ); + } + default: + this.logService.error("NativeMessage, got unknown command: " + message.command); + break; + } + } + + private async send(message: any, appId: string) { + message.timestamp = Date.now(); + + const encrypted = await this.encryptService.encrypt( + JSON.stringify(message), + SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)), + ); + + ipc.platform.nativeMessaging.sendMessage({ appId: appId, message: encrypted }); + } + + private async secureCommunication(remotePublicKey: Uint8Array, appId: string) { + const secret = await this.cryptoFunctionService.randomBytes(64); + await ipc.platform.ephemeralStore.setEphemeralValue( + appId, + new SymmetricCryptoKey(secret).keyB64, + ); + + const encryptedSecret = await this.cryptoFunctionService.rsaEncrypt( + secret, + remotePublicKey, + HashAlgorithmForAsymmetricEncryption, + ); + ipc.platform.nativeMessaging.sendMessage({ + appId: appId, + command: "setupEncryption", + sharedSecret: Utils.fromBufferToB64(encryptedSecret), + }); + } +} diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/duckduckgo-message-handler.service.ts similarity index 95% rename from apps/desktop/src/services/native-message-handler.service.ts rename to apps/desktop/src/services/duckduckgo-message-handler.service.ts index a99effce9e..db42f7b4de 100644 --- a/apps/desktop/src/services/native-message-handler.service.ts +++ b/apps/desktop/src/services/duckduckgo-message-handler.service.ts @@ -26,8 +26,8 @@ const HashAlgorithmForAsymmetricEncryption = "sha1"; // This service handles messages using the protocol created for the DuckDuckGo integration. @Injectable() -export class NativeMessageHandlerService { - private ddgSharedSecret: SymmetricCryptoKey; +export class DuckDuckGoMessageHandlerService { + private duckduckgoSharedSecret: SymmetricCryptoKey; constructor( private stateService: StateService, @@ -109,7 +109,7 @@ export class NativeMessageHandlerService { } const secret = await this.cryptoFunctionService.randomBytes(64); - this.ddgSharedSecret = new SymmetricCryptoKey(secret); + this.duckduckgoSharedSecret = new SymmetricCryptoKey(secret); const sharedKeyB64 = new SymmetricCryptoKey(secret).keyB64; await this.stateService.setDuckDuckGoSharedKey(sharedKeyB64); @@ -166,7 +166,7 @@ export class NativeMessageHandlerService { } private async decryptPayload(message: EncryptedMessage): Promise { - if (!this.ddgSharedSecret) { + if (!this.duckduckgoSharedSecret) { const storedKey = await this.stateService.getDuckDuckGoSharedKey(); if (storedKey == null) { this.sendResponse({ @@ -178,13 +178,13 @@ export class NativeMessageHandlerService { }); return; } - this.ddgSharedSecret = SymmetricCryptoKey.fromJSON({ keyB64: storedKey }); + this.duckduckgoSharedSecret = SymmetricCryptoKey.fromJSON({ keyB64: storedKey }); } try { let decryptedResult = await this.encryptService.decryptToUtf8( message.encryptedCommand as EncString, - this.ddgSharedSecret, + this.duckduckgoSharedSecret, "ddg-shared-key", ); @@ -207,7 +207,7 @@ export class NativeMessageHandlerService { originalMessage: EncryptedMessage, response: DecryptedCommandData, ) { - if (!this.ddgSharedSecret) { + if (!this.duckduckgoSharedSecret) { this.sendResponse({ messageId: originalMessage.messageId, version: NativeMessagingVersion.Latest, @@ -219,7 +219,7 @@ export class NativeMessageHandlerService { return; } - const encryptedPayload = await this.encryptPayload(response, this.ddgSharedSecret); + const encryptedPayload = await this.encryptPayload(response, this.duckduckgoSharedSecret); this.sendResponse({ messageId: originalMessage.messageId, diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 2312bfb2f6..11dc35f95e 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -1,48 +1,16 @@ -import { Injectable, NgZone } from "@angular/core"; -import { firstValueFrom, map } from "rxjs"; +import { Injectable } from "@angular/core"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -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 { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; -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 { UserId } from "@bitwarden/common/types/guid"; -import { DialogService } from "@bitwarden/components"; -import { KeyService, BiometricsService, BiometricStateService } from "@bitwarden/key-management"; - -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"; -import { DesktopSettingsService } from "../platform/services/desktop-settings.service"; -import { NativeMessageHandlerService } from "./native-message-handler.service"; - -const MessageValidTimeout = 10 * 1000; -const HashAlgorithmForAsymmetricEncryption = "sha1"; +import { BiometricMessageHandlerService } from "./biometric-message-handler.service"; +import { DuckDuckGoMessageHandlerService } from "./duckduckgo-message-handler.service"; @Injectable() export class NativeMessagingService { constructor( - private cryptoFunctionService: CryptoFunctionService, - private keyService: KeyService, - private encryptService: EncryptService, - private logService: LogService, - private messagingService: MessagingService, - private desktopSettingService: DesktopSettingsService, - private biometricStateService: BiometricStateService, - private biometricsService: BiometricsService, - private nativeMessageHandler: NativeMessageHandlerService, - private dialogService: DialogService, - private accountService: AccountService, - private authService: AuthService, - private ngZone: NgZone, + private duckduckgoMessageHandler: DuckDuckGoMessageHandlerService, + private biometricMessageHandler: BiometricMessageHandlerService, ) {} init() { @@ -53,202 +21,11 @@ export class NativeMessagingService { const outerMessage = msg as Message; if (outerMessage.version) { // If there is a version, it is a using the protocol created for the DuckDuckGo integration - await this.nativeMessageHandler.handleMessage(outerMessage); + await this.duckduckgoMessageHandler.handleMessage(outerMessage); + return; + } else { + await this.biometricMessageHandler.handleMessage(msg as LegacyMessageWrapper); return; } - - const { appId, message: rawMessage } = msg as LegacyMessageWrapper; - - // Request to setup secure encryption - if ("command" in rawMessage && rawMessage.command === "setupEncryption") { - const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey); - - // Validate the UserId to ensure we are logged into the same account. - const accounts = await firstValueFrom(this.accountService.accounts$); - const userIds = Object.keys(accounts); - if (!userIds.includes(rawMessage.userId)) { - ipc.platform.nativeMessaging.sendMessage({ - command: "wrongUserId", - appId: appId, - }); - return; - } - - if (await firstValueFrom(this.desktopSettingService.browserIntegrationFingerprintEnabled$)) { - ipc.platform.nativeMessaging.sendMessage({ - command: "verifyFingerprint", - appId: appId, - }); - - const fingerprint = await this.keyService.getFingerprint( - rawMessage.userId, - remotePublicKey, - ); - - this.messagingService.send("setFocus"); - - const dialogRef = this.ngZone.run(() => - BrowserSyncVerificationDialogComponent.open(this.dialogService, { fingerprint }), - ); - - const browserSyncVerified = await firstValueFrom(dialogRef.closed); - - if (browserSyncVerified !== true) { - return; - } - } - - await this.secureCommunication(remotePublicKey, appId); - return; - } - - if ((await ipc.platform.ephemeralStore.getEphemeralValue(appId)) == null) { - ipc.platform.nativeMessaging.sendMessage({ - command: "invalidateEncryption", - appId: appId, - }); - return; - } - - const message: LegacyMessage = JSON.parse( - await this.encryptService.decryptToUtf8( - rawMessage as EncString, - SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)), - `native-messaging-session-${appId}`, - ), - ); - - // Shared secret is invalidated, force re-authentication - if (message == null) { - ipc.platform.nativeMessaging.sendMessage({ - command: "invalidateEncryption", - appId: appId, - }); - return; - } - - if (Math.abs(message.timestamp - Date.now()) > MessageValidTimeout) { - this.logService.error("NativeMessage is to old, ignoring."); - return; - } - - switch (message.command) { - case "biometricUnlock": { - const isTemporarilyDisabled = - (await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId)) && - !(await this.biometricsService.supportsBiometric()); - if (isTemporarilyDisabled) { - return this.send({ command: "biometricUnlock", response: "not available" }, appId); - } - - if (!(await this.biometricsService.supportsBiometric())) { - return this.send({ command: "biometricUnlock", response: "not supported" }, appId); - } - - const userId = - (message.userId as UserId) ?? - (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))); - - if (userId == null) { - return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId); - } - - const biometricUnlockPromise = - message.userId == null - ? firstValueFrom(this.biometricStateService.biometricUnlockEnabled$) - : this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId); - if (!(await biometricUnlockPromise)) { - await this.send({ command: "biometricUnlock", response: "not enabled" }, appId); - - return this.ngZone.run(() => - this.dialogService.openSimpleDialog({ - type: "warning", - title: { key: "biometricsNotEnabledTitle" }, - content: { key: "biometricsNotEnabledDesc" }, - cancelButtonText: null, - acceptButtonText: { key: "cancel" }, - }), - ); - } - - try { - const userKey = await this.keyService.getUserKeyFromStorage( - KeySuffixOptions.Biometric, - message.userId, - ); - - if (userKey != null) { - await this.send( - { - command: "biometricUnlock", - response: "unlocked", - userKeyB64: userKey.keyB64, - }, - appId, - ); - - const currentlyActiveAccountId = ( - await firstValueFrom(this.accountService.activeAccount$) - ).id; - const isCurrentlyActiveAccountUnlocked = - (await this.authService.getAuthStatus(userId)) == AuthenticationStatus.Unlocked; - - // prevent proc reloading an active account, when it is the same as the browser - if (currentlyActiveAccountId != message.userId || !isCurrentlyActiveAccountUnlocked) { - await ipc.platform.reloadProcess(); - } - } else { - await this.send({ command: "biometricUnlock", response: "canceled" }, appId); - } - } catch (e) { - await this.send({ command: "biometricUnlock", response: "canceled" }, appId); - } - - break; - } - case "biometricUnlockAvailable": { - const isAvailable = await this.biometricsService.supportsBiometric(); - return this.send( - { - command: "biometricUnlockAvailable", - response: isAvailable ? "available" : "not available", - }, - appId, - ); - } - default: - this.logService.error("NativeMessage, got unknown command: " + message.command); - break; - } - } - - private async send(message: any, appId: string) { - message.timestamp = Date.now(); - - const encrypted = await this.encryptService.encrypt( - JSON.stringify(message), - SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)), - ); - - ipc.platform.nativeMessaging.sendMessage({ appId: appId, message: encrypted }); - } - - private async secureCommunication(remotePublicKey: Uint8Array, appId: string) { - const secret = await this.cryptoFunctionService.randomBytes(64); - await ipc.platform.ephemeralStore.setEphemeralValue( - appId, - new SymmetricCryptoKey(secret).keyB64, - ); - - const encryptedSecret = await this.cryptoFunctionService.rsaEncrypt( - secret, - remotePublicKey, - HashAlgorithmForAsymmetricEncryption, - ); - ipc.platform.nativeMessaging.sendMessage({ - appId: appId, - command: "setupEncryption", - sharedSecret: Utils.fromBufferToB64(encryptedSecret), - }); } } diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts index 015a7c6b21..6a3ad8d62e 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/add-edit.component.ts @@ -2,6 +2,7 @@ import { DatePipe } from "@angular/common"; import { Component, NgZone, OnChanges, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { NgForm } from "@angular/forms"; import { sshagent as sshAgent } from "desktop_native/napi"; +import { lastValueFrom } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/vault/components/add-edit.component"; @@ -22,6 +23,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService, ToastService } from "@bitwarden/components"; +import { SshKeyPasswordPromptComponent } from "@bitwarden/importer/ui"; import { PasswordRepromptService } from "@bitwarden/vault"; const BroadcasterSubscriptionId = "AddEditComponent"; @@ -170,42 +172,64 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On } } - async importSshKeyFromClipboard() { + async importSshKeyFromClipboard(password: string = "") { const key = await this.platformUtilsService.readFromClipboard(); - const parsedKey = await ipc.platform.sshAgent.importKey(key, ""); - if (parsedKey == null || parsedKey.status === sshAgent.SshKeyImportStatus.ParsingError) { + const parsedKey = await ipc.platform.sshAgent.importKey(key, password); + if (parsedKey == null) { this.toastService.showToast({ variant: "error", title: "", message: this.i18nService.t("invalidSshKey"), }); return; - } else if (parsedKey.status === sshAgent.SshKeyImportStatus.UnsupportedKeyType) { - this.toastService.showToast({ - variant: "error", - title: "", - message: this.i18nService.t("sshKeyTypeUnsupported"), - }); - } else if ( - parsedKey.status === sshAgent.SshKeyImportStatus.PasswordRequired || - parsedKey.status === sshAgent.SshKeyImportStatus.WrongPassword - ) { - this.toastService.showToast({ - variant: "error", - title: "", - message: this.i18nService.t("sshKeyPasswordUnsupported"), - }); - return; - } else { - this.cipher.sshKey.privateKey = parsedKey.sshKey.privateKey; - this.cipher.sshKey.publicKey = parsedKey.sshKey.publicKey; - this.cipher.sshKey.keyFingerprint = parsedKey.sshKey.keyFingerprint; - this.toastService.showToast({ - variant: "success", - title: "", - message: this.i18nService.t("sshKeyPasted"), - }); } + + switch (parsedKey.status) { + case sshAgent.SshKeyImportStatus.ParsingError: + this.toastService.showToast({ + variant: "error", + title: "", + message: this.i18nService.t("invalidSshKey"), + }); + return; + case sshAgent.SshKeyImportStatus.UnsupportedKeyType: + this.toastService.showToast({ + variant: "error", + title: "", + message: this.i18nService.t("sshKeyTypeUnsupported"), + }); + return; + case sshAgent.SshKeyImportStatus.PasswordRequired: + case sshAgent.SshKeyImportStatus.WrongPassword: + if (password !== "") { + this.toastService.showToast({ + variant: "error", + title: "", + message: this.i18nService.t("sshKeyWrongPassword"), + }); + } else { + password = await this.getSshKeyPassword(); + await this.importSshKeyFromClipboard(password); + } + return; + default: + this.cipher.sshKey.privateKey = parsedKey.sshKey.privateKey; + this.cipher.sshKey.publicKey = parsedKey.sshKey.publicKey; + this.cipher.sshKey.keyFingerprint = parsedKey.sshKey.keyFingerprint; + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("sshKeyPasted"), + }); + } + } + + async getSshKeyPassword(): Promise { + const dialog = this.dialogService.open(SshKeyPasswordPromptComponent, { + ariaModal: true, + }); + + return await lastValueFrom(dialog.closed); } async typeChange() { diff --git a/apps/web/package.json b/apps/web/package.json index 02ee6babe9..f9d6647a1a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2024.11.1", + "version": "2024.12.0", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.html b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.html index 9a4ce89671..bb5294ebf0 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.html +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-delete-dialog.component.html @@ -8,7 +8,7 @@ 0 && !error"> - {{ "deleteOrganizationUserWarning" | i18n }} + {{ "deleteManyOrganizationUsersWarningDesc" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html index 8b921d6981..8727148f4f 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove-dialog.component.html @@ -1,4 +1,4 @@ - + {{ "noSelectedUsersApplicable" | i18n }} @@ -79,7 +79,7 @@ [disabled]="loading" [bitAction]="submit" > - {{ "removeUsers" | i18n }} + {{ "removeMembers" | i18n }} {{ "close" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.html b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.html index d05fed4f92..3414cc8d32 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.html +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.html @@ -1,7 +1,14 @@ - + - {{ bulkTitle }} + {{ bulkMemberTitle }} + + {{ bulkTitle }} + + {{ "noSelectedUsersApplicable" | i18n }} @@ -11,15 +18,81 @@ {{ error }} - - 0 && !error && isRevoking"> - {{ "revokeUsersWarning" | i18n }} - - {{ "removeMembersWithoutMasterPasswordWarning" | i18n }} - - + + {{ "nonCompliantMembersError" | i18n }} + - + + + 0 && !error && isRevoking"> + {{ "revokeMembersWarning" | i18n }} + + + {{ "claimedAccountRevoke" | i18n }} + + + {{ "unclaimedAccountRevoke" | i18n }} + + + + {{ "restoreMembersInstructions" | i18n }} + + + {{ "removeMembersWithoutMasterPasswordWarning" | i18n }} + + + + + + 0 && !error && isRevoking"> + {{ "revokeUsersWarning" | i18n }} + + + + + + + {{ "member" | i18n }} + {{ "details" | i18n }} + {{ "details" | i18n }} + + + + + + + + + + + {{ user.email }} + {{ user.name }} + + + + + {{ + user.managedByOrganization ? ("claimedAccount" | i18n) : ("unclaimedAccount" | i18n) + }} + + + + - + + + {{ "noMasterPassword" | i18n }} + + + + + + + + {{ "user" | i18n }} @@ -50,21 +123,55 @@ - + - {{ "user" | i18n }} - {{ "status" | i18n }} + {{ "member" | i18n }} + {{ "status" | i18n }} - + + + + + + {{ user.email }} + {{ user.name }} + + - - {{ user.email }} - {{ user.name }} + + {{ statuses.get(user.id) }} + + + {{ "bulkFilteredMessage" | i18n }} + + + + + + + + + {{ "member" | i18n }} + {{ "status" | i18n }} + + + + + + + + + + + {{ user.email }} + {{ user.name }} + + {{ statuses.get(user.id) }} @@ -79,7 +186,7 @@ 0" [bitAction]="submit"> - {{ bulkTitle }} + {{ accountDeprovisioning.enabled ? bulkMemberTitle : bulkTitle }} {{ "close" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.ts index 0ac413eb82..d2bbe4cc40 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-restore-revoke.component.ts @@ -1,8 +1,11 @@ import { DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; +import { Observable } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DialogService } from "@bitwarden/components"; @@ -29,10 +32,13 @@ export class BulkRestoreRevokeComponent { done = false; error: string; showNoMasterPasswordWarning = false; + nonCompliantMembers: boolean = false; + accountDeprovisioningEnabled$: Observable; constructor( protected i18nService: I18nService, private organizationUserApiService: OrganizationUserApiService, + private configService: ConfigService, @Inject(DIALOG_DATA) protected data: BulkRestoreDialogParams, ) { this.isRevoking = data.isRevoking; @@ -41,6 +47,9 @@ export class BulkRestoreRevokeComponent { this.showNoMasterPasswordWarning = this.users.some( (u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false, ); + this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$( + FeatureFlag.AccountDeprovisioning, + ); } get bulkTitle() { @@ -48,14 +57,26 @@ export class BulkRestoreRevokeComponent { return this.i18nService.t(titleKey); } + get bulkMemberTitle() { + const titleKey = this.isRevoking ? "revokeMembers" : "restoreMembers"; + return this.i18nService.t(titleKey); + } + submit = async () => { try { const response = await this.performBulkUserAction(); const bulkMessage = this.isRevoking ? "bulkRevokedMessage" : "bulkRestoredMessage"; - response.data.forEach((entry) => { - const error = entry.error !== "" ? entry.error : this.i18nService.t(bulkMessage); + + response.data.forEach(async (entry) => { + const error = + entry.error !== "" + ? this.i18nService.t("cannotRestoreAccessError") + : this.i18nService.t(bulkMessage); this.statuses.set(entry.id, error); + if (entry.error !== "") { + this.nonCompliantMembers = true; + } }); this.done = true; } catch (e) { diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts index 7bcae82cfd..87037304f5 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts @@ -21,6 +21,7 @@ export interface BulkUserDetails { email: string; status: OrganizationUserStatusType | ProviderUserStatusType; hasMasterPassword?: boolean; + managedByOrganization?: boolean; } type BulkStatusEntry = { diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 04df2957f9..fe84c5097d 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -579,7 +579,10 @@ export class MemberDialogComponent implements OnDestroy { key: "deleteOrganizationUser", placeholders: [this.params.name], }, - content: { key: "deleteOrganizationUserWarning" }, + content: { + key: "deleteOrganizationUserWarningDesc", + placeholders: [this.params.name], + }, type: "warning", acceptButtonText: { key: "delete" }, cancelButtonText: { key: "cancel" }, diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 371314d1ca..b33807c66c 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -742,7 +742,10 @@ export class MembersComponent extends BaseMembersComponent key: "deleteOrganizationUser", placeholders: [this.userNamePipe.transform(user)], }, - content: { key: "deleteOrganizationUserWarning" }, + content: { + key: "deleteOrganizationUserWarningDesc", + placeholders: [this.userNamePipe.transform(user)], + }, type: "warning", acceptButtonText: { key: "delete" }, cancelButtonText: { key: "cancel" }, 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 a36b267e2f..538cc45ac6 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 @@ -62,13 +62,6 @@ const routes: Routes = [ (m) => m.OrganizationReportingModule, ), }, - { - path: "access-intelligence", - loadChildren: () => - import("../../tools/access-intelligence/access-intelligence.module").then( - (m) => m.AccessIntelligenceModule, - ), - }, { path: "billing", loadChildren: () => diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html index f656d488e0..671083a231 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html @@ -15,7 +15,13 @@ - + {{ "save" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts index 644ec1d514..6a869aff22 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts @@ -8,6 +8,7 @@ import { ViewContainerRef, } from "@angular/core"; import { FormBuilder } from "@angular/forms"; +import { Observable, map } from "rxjs"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -42,7 +43,7 @@ export class PolicyEditComponent implements AfterViewInit { policyType = PolicyType; loading = true; enabled = false; - saveDisabled = false; + saveDisabled$: Observable; defaultTypes: any[]; policyComponent: BasePolicyComponent; @@ -73,7 +74,9 @@ export class PolicyEditComponent implements AfterViewInit { this.policyComponent.policy = this.data.policy; this.policyComponent.policyResponse = this.policyResponse; - this.saveDisabled = !this.policyResponse.canToggleState; + this.saveDisabled$ = this.policyComponent.data.statusChanges.pipe( + map((status) => status !== "VALID" || !this.policyResponse.canToggleState), + ); this.cdr.detectChanges(); } diff --git a/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts b/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts index 09ad27e6dc..b0a60a4d81 100644 --- a/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts @@ -104,6 +104,9 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy { title: this.i18nService.t("errorOccured"), message: this.i18nService.t("offerNoLongerValid"), }); + + await this.router.navigate(["/"]); + return; } else { this.badToken = !this.preValidateSponsorshipResponse.isTokenValid; } diff --git a/apps/web/src/app/auth/settings/account/profile.component.html b/apps/web/src/app/auth/settings/account/profile.component.html index e6b6980733..d2a887c9b9 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.html +++ b/apps/web/src/app/auth/settings/account/profile.component.html @@ -37,7 +37,7 @@ - {{ "accountIsManagedMessage" | i18n: managingOrganization?.name }} + {{ "accountIsOwnedMessage" | i18n: managingOrganization?.name }} diff --git a/apps/web/src/app/billing/services/free-families-policy.service.ts b/apps/web/src/app/billing/services/free-families-policy.service.ts new file mode 100644 index 0000000000..cc53e0a32b --- /dev/null +++ b/apps/web/src/app/billing/services/free-families-policy.service.ts @@ -0,0 +1,125 @@ +import { Injectable } from "@angular/core"; +import { combineLatest, filter, from, map, Observable, of, switchMap } from "rxjs"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +interface EnterpriseOrgStatus { + isFreeFamilyPolicyEnabled: boolean; + belongToOneEnterpriseOrgs: boolean; + belongToMultipleEnterpriseOrgs: boolean; +} + +@Injectable({ providedIn: "root" }) +export class FreeFamiliesPolicyService { + protected enterpriseOrgStatus: EnterpriseOrgStatus = { + isFreeFamilyPolicyEnabled: false, + belongToOneEnterpriseOrgs: false, + belongToMultipleEnterpriseOrgs: false, + }; + + constructor( + private policyService: PolicyService, + private organizationService: OrganizationService, + private configService: ConfigService, + ) {} + + get showFreeFamilies$(): Observable { + return this.isFreeFamilyFlagEnabled$.pipe( + switchMap((isFreeFamilyFlagEnabled) => + isFreeFamilyFlagEnabled + ? this.getFreeFamiliesVisibility$() + : this.organizationService.canManageSponsorships$, + ), + ); + } + + private getFreeFamiliesVisibility$(): Observable { + return combineLatest([ + this.checkEnterpriseOrganizationsAndFetchPolicy(), + this.organizationService.canManageSponsorships$, + ]).pipe( + map(([orgStatus, canManageSponsorships]) => + this.shouldShowFreeFamilyLink(orgStatus, canManageSponsorships), + ), + ); + } + + private shouldShowFreeFamilyLink( + orgStatus: EnterpriseOrgStatus | null, + canManageSponsorships: boolean, + ): boolean { + if (!orgStatus) { + return false; + } + const { belongToOneEnterpriseOrgs, isFreeFamilyPolicyEnabled } = orgStatus; + return canManageSponsorships && !(belongToOneEnterpriseOrgs && isFreeFamilyPolicyEnabled); + } + + checkEnterpriseOrganizationsAndFetchPolicy(): Observable { + return this.organizationService.organizations$.pipe( + filter((organizations) => Array.isArray(organizations) && organizations.length > 0), + switchMap((organizations) => this.fetchEnterpriseOrganizationPolicy(organizations)), + ); + } + + private fetchEnterpriseOrganizationPolicy( + organizations: Organization[], + ): Observable { + const { belongToOneEnterpriseOrgs, belongToMultipleEnterpriseOrgs } = + this.evaluateEnterpriseOrganizations(organizations); + + if (!belongToOneEnterpriseOrgs) { + return of({ + isFreeFamilyPolicyEnabled: false, + belongToOneEnterpriseOrgs, + belongToMultipleEnterpriseOrgs, + }); + } + + const organizationId = this.getOrganizationIdForOneEnterprise(organizations); + if (!organizationId) { + return of({ + isFreeFamilyPolicyEnabled: false, + belongToOneEnterpriseOrgs, + belongToMultipleEnterpriseOrgs, + }); + } + + return this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy).pipe( + map((policies) => ({ + isFreeFamilyPolicyEnabled: policies.some( + (policy) => policy.organizationId === organizationId && policy.enabled, + ), + belongToOneEnterpriseOrgs, + belongToMultipleEnterpriseOrgs, + })), + ); + } + + private evaluateEnterpriseOrganizations(organizations: any[]): { + belongToOneEnterpriseOrgs: boolean; + belongToMultipleEnterpriseOrgs: boolean; + } { + const enterpriseOrganizations = organizations.filter((org) => org.canManageSponsorships); + const count = enterpriseOrganizations.length; + + return { + belongToOneEnterpriseOrgs: count === 1, + belongToMultipleEnterpriseOrgs: count > 1, + }; + } + + private getOrganizationIdForOneEnterprise(organizations: any[]): string | null { + const enterpriseOrganizations = organizations.filter((org) => org.canManageSponsorships); + return enterpriseOrganizations.length === 1 ? enterpriseOrganizations[0].id : null; + } + + private get isFreeFamilyFlagEnabled$(): Observable { + return from(this.configService.getFeatureFlag(FeatureFlag.DisableFreeFamiliesSponsorship)); + } +} diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.ts b/apps/web/src/app/billing/settings/sponsored-families.component.ts index c098b6044c..f49e7acce2 100644 --- a/apps/web/src/app/billing/settings/sponsored-families.component.ts +++ b/apps/web/src/app/billing/settings/sponsored-families.component.ts @@ -8,13 +8,17 @@ import { AsyncValidatorFn, ValidationErrors, } from "@angular/forms"; -import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; +import { combineLatest, firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { PlanSponsorshipType } from "@bitwarden/common/billing/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -31,6 +35,7 @@ interface RequestSponsorshipForm { }) export class SponsoredFamiliesComponent implements OnInit, OnDestroy { loading = false; + isFreeFamilyFlagEnabled: boolean; availableSponsorshipOrgs$: Observable; activeSponsorshipOrgs$: Observable; @@ -53,6 +58,8 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { private formBuilder: FormBuilder, private accountService: AccountService, private toastService: ToastService, + private configService: ConfigService, + private policyService: PolicyService, ) { this.sponsorshipForm = this.formBuilder.group({ selectedSponsorshipOrgId: new FormControl("", { @@ -72,10 +79,34 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy { } async ngOnInit() { - this.availableSponsorshipOrgs$ = this.organizationService.organizations$.pipe( - map((orgs) => orgs.filter((o) => o.familySponsorshipAvailable)), + this.isFreeFamilyFlagEnabled = await this.configService.getFeatureFlag( + FeatureFlag.DisableFreeFamiliesSponsorship, ); + if (this.isFreeFamilyFlagEnabled) { + this.availableSponsorshipOrgs$ = combineLatest([ + this.organizationService.organizations$, + this.policyService.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy), + ]).pipe( + map(([organizations, policies]) => + organizations + .filter((org) => org.familySponsorshipAvailable) + .map((org) => ({ + organization: org, + isPolicyEnabled: policies.some( + (policy) => policy.organizationId === org.id && policy.enabled, + ), + })) + .filter(({ isPolicyEnabled }) => !isPolicyEnabled) + .map(({ organization }) => organization), + ), + ); + } else { + this.availableSponsorshipOrgs$ = this.organizationService.organizations$.pipe( + map((orgs) => orgs.filter((o) => o.familySponsorshipAvailable)), + ); + } + this.availableSponsorshipOrgs$.pipe(takeUntil(this._destroy)).subscribe((orgs) => { if (orgs.length === 1) { this.sponsorshipForm.patchValue({ diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.html b/apps/web/src/app/billing/settings/sponsoring-org-row.component.html index b07cbbfad1..eeeaa25604 100644 --- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.html +++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.html @@ -18,7 +18,11 @@ diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts index 06dc1490e3..77fed7b216 100644 --- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts +++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.ts @@ -1,9 +1,13 @@ import { formatDate } from "@angular/common"; import { Component, EventEmitter, Input, Output, OnInit } from "@angular/core"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map, Observable } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { 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"; @@ -21,7 +25,8 @@ export class SponsoringOrgRowComponent implements OnInit { statusMessage = "loading"; statusClass: "tw-text-success" | "tw-text-danger" = "tw-text-success"; - + isFreeFamilyPolicyEnabled$: Observable; + isFreeFamilyFlagEnabled: boolean; private locale = ""; constructor( @@ -31,6 +36,8 @@ export class SponsoringOrgRowComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private dialogService: DialogService, private toastService: ToastService, + private configService: ConfigService, + private policyService: PolicyService, ) {} async ngOnInit() { @@ -42,6 +49,23 @@ export class SponsoringOrgRowComponent implements OnInit { this.sponsoringOrg.familySponsorshipValidUntil, this.sponsoringOrg.familySponsorshipLastSyncDate, ); + this.isFreeFamilyFlagEnabled = await this.configService.getFeatureFlag( + FeatureFlag.DisableFreeFamiliesSponsorship, + ); + + if (this.isFreeFamilyFlagEnabled) { + this.isFreeFamilyPolicyEnabled$ = this.policyService + .getAll$(PolicyType.FreeFamiliesSponsorshipPolicy) + .pipe( + map( + (policies) => + Array.isArray(policies) && + policies.some( + (policy) => policy.organizationId === this.sponsoringOrg.id && policy.enabled, + ), + ), + ); + } } async revokeSponsorship() { diff --git a/apps/web/src/app/billing/shared/billing-free-families-nav-item.component.html b/apps/web/src/app/billing/shared/billing-free-families-nav-item.component.html new file mode 100644 index 0000000000..6958cf3a10 --- /dev/null +++ b/apps/web/src/app/billing/shared/billing-free-families-nav-item.component.html @@ -0,0 +1,5 @@ + diff --git a/apps/web/src/app/billing/shared/billing-free-families-nav-item.component.ts b/apps/web/src/app/billing/shared/billing-free-families-nav-item.component.ts new file mode 100644 index 0000000000..ad32264527 --- /dev/null +++ b/apps/web/src/app/billing/shared/billing-free-families-nav-item.component.ts @@ -0,0 +1,22 @@ +import { Component } from "@angular/core"; +import { Observable } from "rxjs"; + +import { NavigationModule } from "@bitwarden/components"; + +import { FreeFamiliesPolicyService } from "../services/free-families-policy.service"; + +import { BillingSharedModule } from "./billing-shared.module"; + +@Component({ + selector: "billing-free-families-nav-item", + templateUrl: "./billing-free-families-nav-item.component.html", + standalone: true, + imports: [NavigationModule, BillingSharedModule], +}) +export class BillingFreeFamiliesNavItemComponent { + showFreeFamilies$: Observable; + + constructor(private freeFamiliesPolicyService: FreeFamiliesPolicyService) { + this.showFreeFamilies$ = this.freeFamiliesPolicyService.showFreeFamilies$; + } +} diff --git a/apps/web/src/app/layouts/user-layout.component.html b/apps/web/src/app/layouts/user-layout.component.html index 4011ac84a7..6a87658f17 100644 --- a/apps/web/src/app/layouts/user-layout.component.html +++ b/apps/web/src/app/layouts/user-layout.component.html @@ -24,11 +24,7 @@ [text]="'emergencyAccess' | i18n" route="settings/emergency-access" > - + diff --git a/apps/web/src/app/layouts/user-layout.component.ts b/apps/web/src/app/layouts/user-layout.component.ts index bd02533233..88096c9b95 100644 --- a/apps/web/src/app/layouts/user-layout.component.ts +++ b/apps/web/src/app/layouts/user-layout.component.ts @@ -1,16 +1,17 @@ import { CommonModule } from "@angular/common"; import { Component, OnInit } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { Observable, combineLatest, concatMap } from "rxjs"; +import { Observable, concatMap, combineLatest } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/platform/sync"; import { IconModule } from "@bitwarden/components"; +import { BillingFreeFamiliesNavItemComponent } from "../billing/shared/billing-free-families-nav-item.component"; + import { PasswordManagerLogo } from "./password-manager-logo"; import { WebLayoutModule } from "./web-layout.module"; @@ -18,16 +19,24 @@ import { WebLayoutModule } from "./web-layout.module"; selector: "app-user-layout", templateUrl: "user-layout.component.html", standalone: true, - imports: [CommonModule, RouterModule, JslibModule, WebLayoutModule, IconModule], + imports: [ + CommonModule, + RouterModule, + JslibModule, + WebLayoutModule, + IconModule, + BillingFreeFamiliesNavItemComponent, + ], }) export class UserLayoutComponent implements OnInit { protected readonly logo = PasswordManagerLogo; + isFreeFamilyFlagEnabled: boolean; protected hasFamilySponsorshipAvailable$: Observable; + protected showSponsoredFamilies$: Observable; protected showSubscription$: Observable; constructor( private platformUtilsService: PlatformUtilsService, - private organizationService: OrganizationService, private apiService: ApiService, private syncService: SyncService, private billingAccountProfileStateService: BillingAccountProfileStateService, @@ -38,8 +47,6 @@ export class UserLayoutComponent implements OnInit { await this.syncService.fullSync(false); - this.hasFamilySponsorshipAvailable$ = this.organizationService.canManageSponsorships$; - // We want to hide the subscription menu for organizations that provide premium. // Except if the user has premium personally or has a billing history. this.showSubscription$ = combineLatest([ diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 8fe724e121..41605ecb15 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from "@angular/core"; import { Route, RouterModule, Routes } from "@angular/router"; +import { TwoFactorTimeoutComponent } from "@bitwarden/angular/auth/components/two-factor-auth/two-factor-auth-expired.component"; import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap"; import { authGuard, @@ -26,6 +27,7 @@ import { LoginSecondaryContentComponent, LockV2Component, LockIcon, + TwoFactorTimeoutIcon, UserLockIcon, LoginViaAuthRequestComponent, DevicesIcon, @@ -506,7 +508,6 @@ const routes: Routes = [ } satisfies AnonLayoutWrapperData, }, ), - { path: "2fa", canActivate: [unauthGuardFn()], @@ -526,6 +527,28 @@ const routes: Routes = [ }, } satisfies RouteDataProperties & AnonLayoutWrapperData, }, + { + path: "2fa-timeout", + canActivate: [unauthGuardFn()], + children: [ + { + path: "", + component: TwoFactorTimeoutComponent, + }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + data: { + pageIcon: TwoFactorTimeoutIcon, + pageTitle: { + key: "authenticationTimeout", + }, + titleId: "authenticationTimeout", + } satisfies RouteDataProperties & AnonLayoutWrapperData, + }, { path: "recover-2fa", canActivate: [unauthGuardFn()], diff --git a/apps/web/src/app/tools/credential-generator/credential-generator.component.html b/apps/web/src/app/tools/credential-generator/credential-generator.component.html index 5df4a24744..72e521a0a9 100644 --- a/apps/web/src/app/tools/credential-generator/credential-generator.component.html +++ b/apps/web/src/app/tools/credential-generator/credential-generator.component.html @@ -2,16 +2,13 @@ - - - {{ "generatorHistory" | i18n }} - - - + + {{ "generatorHistory" | i18n }} + diff --git a/apps/web/src/app/tools/credential-generator/credential-generator.component.ts b/apps/web/src/app/tools/credential-generator/credential-generator.component.ts index f252796d06..8d7b56a09a 100644 --- a/apps/web/src/app/tools/credential-generator/credential-generator.component.ts +++ b/apps/web/src/app/tools/credential-generator/credential-generator.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; -import { ButtonModule, DialogService, ItemModule, LinkModule } from "@bitwarden/components"; +import { ButtonModule, DialogService, LinkModule } from "@bitwarden/components"; import { CredentialGeneratorHistoryDialogComponent, GeneratorModule, @@ -13,7 +13,7 @@ import { SharedModule } from "../../shared"; standalone: true, selector: "credential-generator", templateUrl: "credential-generator.component.html", - imports: [SharedModule, HeaderModule, GeneratorModule, ItemModule, ButtonModule, LinkModule], + imports: [SharedModule, HeaderModule, GeneratorModule, ButtonModule, LinkModule], }) export class CredentialGeneratorComponent { constructor(private dialogService: DialogService) {} diff --git a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts index 85faac0c08..527f00d58f 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts @@ -130,6 +130,8 @@ export class AddEditComponentV2 implements OnInit { return this.i18nService.t(partOne, this.i18nService.t("typeIdentity").toLowerCase()); case CipherType.SecureNote: return this.i18nService.t(partOne, this.i18nService.t("note").toLowerCase()); + case CipherType.SshKey: + return this.i18nService.t(partOne, this.i18nService.t("typeSshKey").toLowerCase()); } } diff --git a/apps/web/src/app/vault/individual-vault/add-edit.component.ts b/apps/web/src/app/vault/individual-vault/add-edit.component.ts index b4bd42a389..8f8c477951 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit.component.ts @@ -100,6 +100,14 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On async ngOnInit() { await super.ngOnInit(); await this.load(); + + // https://bitwarden.atlassian.net/browse/PM-10413 + // cannot generate ssh keys so block creation + if (this.type === CipherType.SshKey && this.cipherId == null) { + this.type = CipherType.Login; + this.cipher.type = CipherType.Login; + } + this.viewOnly = !this.cipher.edit && this.editMode; // remove when all the title for all clients are updated to New Item if (this.cloneMode || !this.editMode) { diff --git a/apps/web/src/app/vault/individual-vault/vault.component.html b/apps/web/src/app/vault/individual-vault/vault.component.html index 679d2ce6f7..75332dcf72 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.html +++ b/apps/web/src/app/vault/individual-vault/vault.component.html @@ -1,4 +1,6 @@ - + ; private activeUserId: UserId; - protected organizationsPaymentStatus: FreeTrial[] = []; private searchText$ = new Subject(); private refresh$ = new BehaviorSubject(null); private destroy$ = new Subject(); @@ -208,6 +208,37 @@ export class VaultComponent implements OnInit, OnDestroy { ), ); + protected organizationsPaymentStatus$: Observable = combineLatest([ + this.organizationService.organizations$.pipe( + map((organizations) => organizations?.filter((org) => org.isOwner) ?? []), + ), + this.hasSubscription$, + ]).pipe( + switchMap(([ownerOrgs, hasSubscription]) => { + if (!ownerOrgs || ownerOrgs.length === 0 || !hasSubscription) { + return of([]); + } + return combineLatest( + ownerOrgs.map((org) => + combineLatest([ + this.organizationApiService.getSubscription(org.id), + this.organizationBillingService.getPaymentSource(org.id), + ]).pipe( + map(([subscription, paymentSource]) => { + return this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( + org, + subscription, + paymentSource, + ); + }), + ), + ), + ); + }), + map((results) => results.filter((result) => result.shownBanner)), + shareReplay({ refCount: false, bufferSize: 1 }), + ); + constructor( private syncService: SyncService, private route: ActivatedRoute, @@ -241,6 +272,7 @@ export class VaultComponent implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, protected billingApiService: BillingApiServiceAbstraction, private trialFlowService: TrialFlowService, + private organizationBillingService: OrganizationBillingServiceAbstraction, ) {} async ngOnInit() { @@ -423,36 +455,6 @@ export class VaultComponent implements OnInit, OnDestroy { this.unpaidSubscriptionDialog$.pipe(takeUntil(this.destroy$)).subscribe(); - const organizationsPaymentStatus$ = combineLatest([ - this.organizationService.organizations$, - this.hasSubscription$, - ]).pipe( - switchMap(([allOrganizations, hasSubscription]) => { - if (!allOrganizations || allOrganizations.length === 0 || !hasSubscription) { - return of([]); - } - return combineLatest( - allOrganizations - .filter((org) => org.isOwner && hasSubscription) - .map((org) => - combineLatest([ - this.organizationApiService.getSubscription(org.id), - this.organizationApiService.getBilling(org.id), - ]).pipe( - map(([subscription, billing]) => { - return this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( - org, - subscription, - billing?.paymentSource, - ); - }), - ), - ), - ); - }), - map((results) => results.filter((result) => result.shownBanner)), - ); - firstSetup$ .pipe( switchMap(() => this.refresh$), @@ -466,7 +468,6 @@ export class VaultComponent implements OnInit, OnDestroy { ciphers$, collections$, selectedCollection$, - organizationsPaymentStatus$, ]), ), takeUntil(this.destroy$), @@ -480,7 +481,6 @@ export class VaultComponent implements OnInit, OnDestroy { ciphers, collections, selectedCollection, - organizationsPaymentStatus, ]) => { this.filter = filter; this.canAccessPremium = canAccessPremium; @@ -496,7 +496,6 @@ export class VaultComponent implements OnInit, OnDestroy { this.showBulkMove = filter.type !== "trash"; this.isEmpty = collections?.length === 0 && ciphers?.length === 0; - this.organizationsPaymentStatus = organizationsPaymentStatus; this.performingInitialLoad = false; this.refreshing = false; }, 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 64318047b9..18cc6e49ab 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -48,6 +48,7 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; import { EventType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -252,6 +253,7 @@ export class VaultComponent implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, private trialFlowService: TrialFlowService, protected billingApiService: BillingApiServiceAbstraction, + private organizationBillingService: OrganizationBillingServiceAbstraction, ) {} async ngOnInit() { @@ -595,15 +597,11 @@ export class VaultComponent implements OnInit, OnDestroy { combineLatest([ of(org), this.organizationApiService.getSubscription(org.id), - this.organizationApiService.getBilling(org.id), + this.organizationBillingService.getPaymentSource(org.id), ]), ), - map(([org, sub, billing]) => { - return this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( - org, - sub, - billing?.paymentSource, - ); + map(([org, sub, paymentSource]) => { + return this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(org, sub, paymentSource); }), ); diff --git a/apps/web/src/connectors/duo-redirect.ts b/apps/web/src/connectors/duo-redirect.ts index 2b8a3de4de..a113c6b975 100644 --- a/apps/web/src/connectors/duo-redirect.ts +++ b/apps/web/src/connectors/duo-redirect.ts @@ -51,14 +51,12 @@ window.addEventListener("load", async () => { */ function redirectToDuoFrameless(redirectUrl: string) { const validateUrl = new URL(redirectUrl); + const validDuoUrl = + validateUrl.protocol === "https:" && + (validateUrl.hostname.endsWith(".duosecurity.com") || + validateUrl.hostname.endsWith(".duofederal.com")); - if ( - validateUrl.protocol !== "https:" || - !( - validateUrl.hostname.endsWith("duosecurity.com") || - validateUrl.hostname.endsWith("duofederal.com") - ) - ) { + if (!validDuoUrl) { throw new Error("Invalid redirect URL"); } diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 0930f9b0e7..d6fe5401d4 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Wagwoordgeskiedenis" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Daar is geen wagwoorde om te lys nie." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Wis", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index 7a5e63d9b8..407ed12cca 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -527,10 +527,10 @@ "description": "Search Secure Note type" }, "searchVault": { - "message": "البحث في الخزنة" + "message": "البحث في الخزانة" }, "searchMyVault": { - "message": "البحث في خزنتي" + "message": "البحث في خزانتي" }, "searchOrganization": { "message": "البحث عن المؤسسة" @@ -789,19 +789,19 @@ "message": "أنا" }, "myVault": { - "message": "خزنتي" + "message": "خزانتي" }, "allVaults": { - "message": "جميع الخزنات" + "message": "جميع الخزانات" }, "vault": { - "message": "الخزنة" + "message": "الخزانة" }, "vaults": { - "message": "الخزنات" + "message": "الخزانات" }, "vaultItems": { - "message": "عناصر الخزنة" + "message": "عناصر الخزانة" }, "filter": { "message": "تصفية" @@ -976,7 +976,7 @@ "message": "لا" }, "loginOrCreateNewAccount": { - "message": "قم بتسجيل الدخول أو أنشئ حساباً جديداً لتتمكن من الوصول إلى خزنتك السرية." + "message": "قم بتسجيل الدخول أو أنشئ حساباً جديداً لتتمكن من الوصول إلى خزانتك السرية." }, "loginWithDevice": { "message": "تسجيل الدخول باستخدام جهاز" @@ -1132,7 +1132,7 @@ "message": "كلمة المرور الرئيسية" }, "masterPassDesc": { - "message": "كلمة المرور الرئيسية هي كلمة المرور التي تستخدمها للوصول إلى خزنتك. من المهم جدا ألا تنسى كلمة المرور الرئيسية. لا توجد طريقة لاسترداد كلمة المرور في حال نسيتها." + "message": "كلمة المرور الرئيسية هي كلمة المرور التي تستخدمها للوصول إلى خزانتك. من المهم جدا ألا تنسى كلمة المرور الرئيسية. لا توجد طريقة لاسترداد كلمة المرور في حال نسيتها." }, "masterPassImportant": { "message": "لا يمكن استعادة كلمة المرور الرئيسية إذا نسيتها!" @@ -1500,7 +1500,7 @@ "message": "تحذير" }, "confirmVaultExport": { - "message": "تأكيد تصدير الخزنة" + "message": "تأكيد تصدير الخزانة" }, "confirmSecretsExport": { "message": "Confirm secrets export" @@ -1524,7 +1524,7 @@ "message": "Export from" }, "exportVault": { - "message": "تصدير الخزنة" + "message": "تصدير الخزانة" }, "exportSecrets": { "message": "Export secrets" @@ -1575,7 +1575,7 @@ "message": "This file is password-protected. Please enter the file password to import data." }, "exportSuccess": { - "message": "تم تصدير بيانات الخزنة الخاصة بك." + "message": "تم تصدير بيانات الخزانة الخاصة بك" }, "passwordGenerator": { "message": "مولّد كلمات المرور" @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "سجل كلمات المرور" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "مسح", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -1777,7 +1795,7 @@ } }, "purgeVault": { - "message": "مسح الخزنة" + "message": "مسح الخزانة" }, "purgedOrganizationVault": { "message": "Purged organization vault." @@ -1940,7 +1958,7 @@ "message": "التفضيلات" }, "preferencesDesc": { - "message": "تخصيص تجرِبة خزنة الويب الخاصة بك." + "message": "تخصيص تجرِبة خزانة الويب الخاصة بك." }, "preferencesUpdated": { "message": "Preferences saved" @@ -1949,7 +1967,7 @@ "message": "اللّغة" }, "languageDesc": { - "message": "تغيير اللغة المستخدمة في خزنة الويب." + "message": "تغيير اللغة المستخدمة في خزانة الويب." }, "enableFavicon": { "message": "إظهار أيقونات الموقع" @@ -4313,7 +4331,7 @@ "message": "عوامل التصفية" }, "vaultTimeout": { - "message": "مهلة الخزنة" + "message": "مهلة الخزانة" }, "vaultTimeout1": { "message": "Timeout" @@ -4446,7 +4464,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": "بصمة الإصبع" @@ -4579,13 +4597,13 @@ "message": "User preference" }, "vaultTimeoutAction": { - "message": "إجراء مهلة الخزنة" + "message": "إجراء مهلة الخزانة" }, "vaultTimeoutActionLockDesc": { - "message": "الخزنة المقفلة تتطلب إعادة إدخال كلمة المرور الرئيسية الخاصة بك للوصول إليها مرة أخرى." + "message": "الخزانة المقفلة تتطلب إعادة إدخال كلمة المرور الرئيسية الخاصة بك للوصول إليها مرة أخرى." }, "vaultTimeoutActionLogOutDesc": { - "message": "تسجيل الخروج من الخزنة يتطلب إعادة المصادقة للوصول إليها مرة أخرى." + "message": "تسجيل الخروج من الخزانة يتطلب إعادة المصادقة للوصول إليها مرة أخرى." }, "lock": { "message": "قفل", @@ -5557,7 +5575,7 @@ "message": "المظهر" }, "themeDesc": { - "message": "اختر مظهر خزنة الويب خاصتك." + "message": "اختر مظهر خزانة الويب خاصتك." }, "themeSystem": { "message": "تلقائي" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 47b0ee377e..5f9a8fda09 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Parol tarixçəsi" }, + "generatorHistory": { + "message": "Yaradıcı tarixçəsi" + }, + "clearGeneratorHistoryTitle": { + "message": "Yaradıcı tarixçəsini təmizlə" + }, + "cleargGeneratorHistoryDescription": { + "message": "Davam etsəniz, yaradıcı tarixçəsindəki bütün girişlər həmişəlik silinəcək. Davam etmək istədiyinizə əminsiniz?" + }, "noPasswordsInList": { "message": "Siyahılanacaq heç bir parol yoxdur." }, + "clearHistory": { + "message": "Tarixçəni təmizlə" + }, + "nothingToShow": { + "message": "Göstəriləcək heç nə yoxdur" + }, + "nothingGeneratedRecently": { + "message": "Təzəlikcə heç nə yaratmamısınız" + }, "clear": { "message": "Təmizlə", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Bütün seansların səlahiyyəti götürüldü" }, - "accountIsManagedMessage": { - "message": "Bu hesab $ORGANIZATIONNAME$ tərəfindən idarə olunur", + "accountIsOwnedMessage": { + "message": "$ORGANIZATIONNAME$ bu hesabın sahibidir", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Giriş başladıldı" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Gələcək girişləri problemsiz etmək üçün bu cihazı xatırla" }, "deviceApprovalRequired": { "message": "Cihaz təsdiqi tələb olunur. Aşağıdan bir təsdiq variantı seçin:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Cihaz təsdiqi tələb olunur" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Aşağıdan bir təsdiq seçimi edin" }, "rememberThisDevice": { "message": "Bu cihazı xatırla" @@ -8293,7 +8311,7 @@ "message": "İstifadəçi e-poçtu əskikdir" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktiv istifadəçi e-poçtu tapılmadı. Hesabınızdan çıxış edilir." }, "deviceTrusted": { "message": "Cihaz güvənlidir" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Təşkilat üçün kolleksiya davranışını idarə et" }, - "limitCollectionCreationDeletionDesc": { - "message": "Kolleksiya yaradılmasını və silinməsini sahibləri və adminləri ilə məhdudlaşdır" - }, "limitCollectionCreationDesc": { "message": "Kolleksiya yaradılmasını sahibləri və adminləri ilə məhdudlaşdır" }, diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 0e709bc33c..ff1d68e970 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Гісторыя пароляў" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "У спісе адсутнічаюць паролі." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Ачысціць", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Аўтарызацыя ўсіх сеансаў скасавана" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index bbdb722984..6df82896f9 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Хронология на паролата" }, + "generatorHistory": { + "message": "История на генерирането" + }, + "clearGeneratorHistoryTitle": { + "message": "Изчистване на историята на генериране" + }, + "cleargGeneratorHistoryDescription": { + "message": "Ако продължите, всички записи в историята на генериране ще бъдат изтрити завинаги. Наистина ли искате това?" + }, "noPasswordsInList": { "message": "Няма пароли за показване." }, + "clearHistory": { + "message": "Изчистване на историята" + }, + "nothingToShow": { + "message": "Няма нищо за показване" + }, + "nothingGeneratedRecently": { + "message": "Скоро не сте генерирали нищо" + }, "clear": { "message": "Изчистване", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Всички сесии са прекратени" }, - "accountIsManagedMessage": { - "message": "Тази регистрация се управлява от $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Тази регистрация е притежание на $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Вписването е стартирано" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Запомняне на това устройство, така че в бъдеще вписването да бъде по-лесно" }, "deviceApprovalRequired": { "message": "Изисква се одобрение на устройството. Изберете начин за одобрение по-долу:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Необходимо е одобрение на устройството" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Изберете начин за одобряване по-долу" }, "rememberThisDevice": { "message": "Запомняне на това устройство" @@ -8293,7 +8311,7 @@ "message": "Липсва е-поща на потребителя" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Не е намерена е-поща на активен потребител. Ще бъдете отписан(а)." }, "deviceTrusted": { "message": "Устройството е доверено" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Управление на поведението на колекциите за организацията" }, - "limitCollectionCreationDeletionDesc": { - "message": "Ограничаване на създаването и изтриването на колекции, така че да може да се извършва само от собствениците и администраторите" - }, "limitCollectionCreationDesc": { "message": "Ограничаване на създаването на колекции, така че да може да се извършва само от собствениците и администраторите" }, diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 6cf67fcee6..1e2fb12f3b 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 9e221e721f..5acd4aa9d6 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 4e5e78f3c5..a1b591f8df 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Historial de les contrasenyes" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "No hi ha cap contrasenya a llistar." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Esborra", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Totes les sessions estan desautoritzades" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Administra el comportament de col·lecció per a l'organització" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limita la creació i la supressió de col·leccions als propietaris i administradors" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 2135f0e01a..147af1e94a 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Historie hesel" }, + "generatorHistory": { + "message": "Historie generátoru" + }, + "clearGeneratorHistoryTitle": { + "message": "Vymazat historii generátoru" + }, + "cleargGeneratorHistoryDescription": { + "message": "Pokud budete pokračovat, všechny položky budou trvale smazány z historie generátoru. Jste si jisti, že chcete pokračovat?" + }, "noPasswordsInList": { "message": "Nejsou k dispozici žádná hesla." }, + "clearHistory": { + "message": "Vymazat historii" + }, + "nothingToShow": { + "message": "Nic k zobrazení" + }, + "nothingGeneratedRecently": { + "message": "Nedávno jste nic nevygenerovali" + }, "clear": { "message": "Vymazat", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Všechny relace byly zrušeny" }, - "accountIsManagedMessage": { - "message": "Tento účet je spravován: $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Tento účet je vlastněn: $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Bylo zahájeno přihlášení" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamatovat si toto zařízení pro bezproblémové budoucí přihlášení" }, "deviceApprovalRequired": { "message": "Vyžaduje se schválení zařízení. Vyberte možnost schválení níže:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Vyžaduje se schválení zařízení" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Vyberte volbu schválení níže" }, "rememberThisDevice": { "message": "Zapamatovat toto zařízení" @@ -8293,7 +8311,7 @@ "message": "Chybí e-mail uživatele" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktivní uživatelský e-mail nebyl nalezen. Budete odhlášeni." }, "deviceTrusted": { "message": "Zařízení zařazeno mezi důvěryhodné" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Spravuje chování kolekce pro organizaci" }, - "limitCollectionCreationDeletionDesc": { - "message": "Omezí vytváření a mazání kolekce na vlastníky a správce" - }, "limitCollectionCreationDesc": { "message": "Omezí vytváření kolekce na vlastníky a správce" }, diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 0839ac7f9f..91d6f860aa 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index e1cb4d28f2..eeb2d86c9f 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Adgangskodehistorik" }, + "generatorHistory": { + "message": "Generatorhistorik" + }, + "clearGeneratorHistoryTitle": { + "message": "Ryd generatorhistorik" + }, + "cleargGeneratorHistoryDescription": { + "message": "Fortsætter man, slettes alle poster permanent fra generatorhistorikken. Sikker på, at handlingen skal udføres?" + }, "noPasswordsInList": { "message": "Der er ingen adgangskoder at vise." }, + "clearHistory": { + "message": "Ryd historik" + }, + "nothingToShow": { + "message": "Intet at vise" + }, + "nothingGeneratedRecently": { + "message": "Intet genereret for nylig" + }, "clear": { "message": "Ryd", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Godkendelser for alle sessioner fjernet" }, - "accountIsManagedMessage": { - "message": "Denne konto håndteres af $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Denne konto ejes af $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Indlogning påbegyndt" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Husk denne enhed for at gøre fremtidige indlogninger gnidningsløse" }, "deviceApprovalRequired": { "message": "Enhedsgodkendelse kræves. Vælg en godkendelsesmulighed nedenfor:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Enhedsgodkendelse kræves" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Vælg en godkendelsesmulighed nedenfor" }, "rememberThisDevice": { "message": "Husk denne enhed" @@ -8293,7 +8311,7 @@ "message": "Brugers e-mail mangler" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktiv bruger e-mail ikke fundet. Man logges ud." }, "deviceTrusted": { "message": "Enhed betroet" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Håndtér samlingsadfærden for organisationen" }, - "limitCollectionCreationDeletionDesc": { - "message": "Begræns samlingsoprettelse og -sletning til ejere og admins" - }, "limitCollectionCreationDesc": { "message": "Begræns samlingsoprettelse til ejere og admins" }, diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 7e79831c54..59fd62a30e 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -985,7 +985,7 @@ "message": "Die Anmeldung über ein Gerät muss in den Einstellungen der Bitwarden App eingerichtet werden. Benötigst du eine andere Option?" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Brauchst du eine andere Option?" }, "loginWithMasterPassword": { "message": "Mit Master-Passwort anmelden" @@ -1309,7 +1309,7 @@ "message": "Eine Benachrichtigung wurde an dein Gerät gesendet" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Stelle sicher, dass dein Konto entsperrt ist und die Fingerabdruck-Phrase mit der vom anderen Gerät übereinstimmt" }, "versionNumber": { "message": "Version $VERSION_NUMBER$", @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Passwortverlauf" }, + "generatorHistory": { + "message": "Generator-Verlauf" + }, + "clearGeneratorHistoryTitle": { + "message": "Generator-Verlauf löschen" + }, + "cleargGeneratorHistoryDescription": { + "message": "Wenn du fortfährst, werden alle Einträge dauerhaft aus dem Generator-Verlauf gelöscht. Bist du sicher, dass du fortfahren möchtest?" + }, "noPasswordsInList": { "message": "Keine Passwörter vorhanden." }, + "clearHistory": { + "message": "Verlauf löschen" + }, + "nothingToShow": { + "message": "Nichts anzuzeigen" + }, + "nothingGeneratedRecently": { + "message": "Du hast in letzter Zeit nichts generiert" + }, "clear": { "message": "Löschen", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Alle Sitzungen wurden abgemeldet" }, - "accountIsManagedMessage": { - "message": "Dieses Konto wird von $ORGANIZATIONNAME$ verwaltet", + "accountIsOwnedMessage": { + "message": "Dieses Konto ist im Besitz von $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -3397,7 +3415,7 @@ } }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Alle Anmeldeoptionen anzeigen" }, "viewAllLoginOptions": { "message": "Alle Anmeldeoptionen anzeigen" @@ -4456,7 +4474,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." }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Du wirst benachrichtigt, sobald die Anfrage genehmigt wurde" }, "free": { "message": "Free", @@ -6392,7 +6410,7 @@ "message": "Erforderlich, wenn die Entitäts-ID keine URL ist." }, "offerNoLongerValid": { - "message": "This offer is no longer valid. Contact your organization administrators for more information." + "message": "Dieses Angebot ist nicht mehr gültig. Kontaktiere die Administratoren deiner Organisation für weitere Informationen." }, "openIdOptionalCustomizations": { "message": "Optionale Anpassungen" @@ -6501,7 +6519,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Benutze $RECOMMENDED$ oder mehr Zeichen um ein starkes Passwort zu generieren.", + "message": " Verwende $RECOMMENDED$ oder mehr Zeichen, um ein starkes Passwort zu generieren.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -6511,7 +6529,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Verwende $RECOMMENDED$ oder mehr Wörter, um eine starke Passphrase zu generieren.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -8052,16 +8070,16 @@ "message": "Anmeldung eingeleitet" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Dieses Gerät merken, um zukünftige Anmeldungen reibungslos zu gestalten" }, "deviceApprovalRequired": { "message": "Geräte-Genehmigung erforderlich. Wähle unten eine Genehmigungsoption aus:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Geräte-Genehmigung erforderlich" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Wähle unten eine Genehmigungsoption aus" }, "rememberThisDevice": { "message": "Dieses Gerät merken" @@ -8293,7 +8311,7 @@ "message": "E-Mail-Adresse des Benutzers fehlt" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktive Benutzer-E-Mail-Adresse nicht gefunden. Du wirst abgemeldet." }, "deviceTrusted": { "message": "Gerät wird vertraut" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Verwalte das Sammlungsverhalten für die Organisation" }, - "limitCollectionCreationDeletionDesc": { - "message": "Sammlungserstellung und -löschung auf Eigentümer und Administratoren beschränken" - }, "limitCollectionCreationDesc": { "message": "Das Erstellen von Sammlungen auf Eigentümer und Administratoren beschränken" }, @@ -9675,7 +9690,7 @@ "message": "Bist du sicher, dass du diesen Anhang dauerhaft löschen möchtest?" }, "manageSubscriptionFromThe": { - "message": "verwalten", + "message": "Verwalte das Abonnement von", "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." }, "toHostBitwardenOnYourOwnServer": { @@ -9759,18 +9774,18 @@ "message": "Erfolgreich gelöscht" }, "freeFamiliesSponsorship": { - "message": "Remove Free Bitwarden Families sponsorship" + "message": "Kostenloses Bitwarden Families-Sponsoring entfernen" }, "freeFamiliesSponsorshipPolicyDesc": { - "message": "Do not allow members to redeem a Families plan through this organization." + "message": "Erlaube Mitgliedern nicht, einen Families-Tarif über diese Organisation einzulösen." }, "verifyBankAccountWithStatementDescriptorWarning": { - "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the organization's billing page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Die Zahlung mit einem Bankkonto ist nur für Kunden in den Vereinigten Staaten möglich. Du musst dein Bankkonto verifizieren. Wir werden innerhalb der nächsten 1-2 Werktage eine Mikro-Einzahlung vornehmen. Gib den Code aus der Beschreibung dieser Einzahlung auf der Rechnungsseite der Organisation ein, um das Bankkonto zu verifizieren. Schlägt die Verifizierung des Bankkontos fehl, wird dies als versäumte Zahlung gewertet und dein Abonnement gesperrt." }, "verifyBankAccountWithStatementDescriptorInstructions": { - "message": "We have made a micro-deposit to your bank account (this may take 1-2 business days). Enter the six-digit code starting with 'SM' found on the deposit description. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Wir haben eine Mikro-Einzahlung auf dein Bankkonto vorgenommen (dies kann 1-2 Werktage dauern). Gib den sechsstelligen Code ein, der mit \"SM\" beginnt und in der Einzahlungsbeschreibung steht. Schlägt die Verifizierung des Bankkontos fehl, wird dies als versäumte Zahlung gewertet und dein Abonnement gesperrt." }, "descriptorCode": { - "message": "Descriptor code" + "message": "Beschreibungscode" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index aa9cb47b13..110286543c 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Ιστορικό Κωδικού" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Δεν υπάρχουν κωδικοί στη λίστα." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Εκκαθάριση", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Η Ανακληθεί η Πρόσβαση από Όλες τις Συνεδρίες" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 0c1558a35d..cab0e703a7 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -29,6 +29,33 @@ "notifiedMembers": { "message": "Notified members" }, + "revokeMembers": { + "message": "Revoke members" + }, + "restoreMembers": { + "message": "Restore members" + }, + "revokeMembersWarning":{ + "message": "Members with claimed and unclaimed accounts will have different results when revoked:" + }, + "claimedAccountRevoke": { + "message": "Claimed account: Revoke access to Bitwarden account" + }, + "unclaimedAccountRevoke": { + "message": "Unclaimed account: Revoke access to organization data" + }, + "claimedAccount": { + "message": "Claimed account" + }, + "unclaimedAccount": { + "message": "Unclaimed account" + }, + "restoreMembersInstructions": { + "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." + }, + "cannotRestoreAccessError":{ + "message": "Cannot restore organization access" + }, "allApplicationsWithCount": { "message": "All applications ($COUNT$)", "placeholders": { @@ -1110,6 +1137,12 @@ "logInToBitwarden": { "message": "Log in to Bitwarden" }, + "authenticationTimeout": { + "message": "Authentication timeout" + }, + "authenticationSessionTimedOut": { + "message": "The authentication session timed out. Please restart the login process." + }, "verifyIdentity": { "message": "Verify your Identity" }, @@ -1785,8 +1818,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -5610,6 +5643,12 @@ "bulkFilteredMessage": { "message": "Excluded, not applicable for this action" }, + "nonCompliantMembersTitle":{ + "message": "Non-compliant members" + }, + "nonCompliantMembersError":{ + "message": "Members that are non-compliant with the Single organization or Two-step login policy cannot be restored until they adhere to the policy requirements" + }, "fingerprint": { "message": "Fingerprint" }, @@ -9715,9 +9754,19 @@ "description": "Title for the delete organization user dialog" } }, - "deleteOrganizationUserWarning": { - "message": "When a member is deleted, their Bitwarden account and individual vault data will be permanently deleted. Collection data will remain in the organization. To reinstate them they must create an account and be onboarded again.", - "description": "Warning for the delete organization user dialog" + "deleteOrganizationUserWarningDesc": { + "message": "This will permanently delete all items owned by $NAME$. Collection items are not impacted.", + "description": "Warning description for the delete organization user dialog", + "placeholders": { + "name": { + "content": "$1", + "example": "John Doe" + } + } + }, + "deleteManyOrganizationUsersWarningDesc": { + "message": "This will permanently delete all items owned by the following members. Collection items are not impacted.", + "description": "Warning description for the bulk delete organization users dialog" }, "organizationUserDeleted": { "message": "Deleted $NAME$", @@ -9787,5 +9836,8 @@ }, "descriptorCode": { "message": "Descriptor code" + }, + "removeMembers": { + "message": "Remove members" } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 9039334134..beadcfc710 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorised" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behaviour for the organisation" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index ba89bea21c..1ca16a1704 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorised" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behaviour for the organisation" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index eab492710a..7c4061ad35 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Pasvorta Historio" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Ne estas listigitaj pasvortoj." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Malplenigi", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Ĉiuj Sesioj Neaŭtorizitaj" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 1a51107dfe..ef1152c04b 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -27,10 +27,10 @@ } }, "notifiedMembers": { - "message": "Notified members" + "message": "Miembros notificados" }, "allApplicationsWithCount": { - "message": "All applications ($COUNT$)", + "message": "Todas las aplicaciones ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -42,7 +42,7 @@ "message": "Create new login item" }, "criticalApplicationsWithCount": { - "message": "Critical applications ($COUNT$)", + "message": "Aplicaciones críticas ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -51,7 +51,7 @@ } }, "notifiedMembersWithCount": { - "message": "Notified members ($COUNT$)", + "message": "Miembros notificados ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -87,7 +87,7 @@ "message": "Apps marked as critical" }, "application": { - "message": "Application" + "message": "Aplicación" }, "atRiskPasswords": { "message": "At-risk passwords" @@ -759,31 +759,31 @@ "message": "Copy website" }, "copyNotes": { - "message": "Copy notes" + "message": "Copiar notas" }, "copyAddress": { - "message": "Copy address" + "message": "Copiar dirección" }, "copyPhone": { - "message": "Copy phone" + "message": "Copiar teléfono" }, "copyEmail": { - "message": "Copy email" + "message": "Copiar correo electrónico" }, "copyCompany": { "message": "Copy company" }, "copySSN": { - "message": "Copy Social Security number" + "message": "Copiar número de seguro social" }, "copyPassportNumber": { - "message": "Copy passport number" + "message": "Copiar número de pasaporte" }, "copyLicenseNumber": { - "message": "Copy license number" + "message": "Copiar número de licencia" }, "copyName": { - "message": "Copy name" + "message": "Copiar nombre" }, "me": { "message": "Yo" @@ -952,7 +952,7 @@ "message": "Restart registration" }, "expiredLink": { - "message": "Expired link" + "message": "Enlace expirado" }, "pleaseRestartRegistrationOrTryLoggingIn": { "message": "Please restart registration or try logging in." @@ -1169,10 +1169,10 @@ "message": "Configuración" }, "accountEmail": { - "message": "Account email" + "message": "Correo electrónico de la cuenta" }, "requestHint": { - "message": "Request hint" + "message": "Solicitar pista" }, "requestPasswordHint": { "message": "Request password hint" @@ -1221,7 +1221,7 @@ "message": "Your new account has been created!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "¡Has iniciado sesión!" }, "trialAccountCreated": { "message": "Cuenta creada con éxito." @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Historial de contraseñas" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "No hay contraseñas que listar." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Limpiar", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Desautorizadas todas las sesiones" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Administrar el comportamiento de la colección para la organización" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, @@ -8654,16 +8669,16 @@ } }, "addField": { - "message": "Add field" + "message": "Añadir campo" }, "editField": { - "message": "Edit field" + "message": "Editar campo" }, "items": { "message": "Elementos" }, "assignedSeats": { - "message": "Assigned seats" + "message": "Asientos asignados" }, "assigned": { "message": "Asignado" @@ -8678,16 +8693,16 @@ "message": "Desvincular organización" }, "manageSeats": { - "message": "MANAGE SEATS" + "message": "GESTIONAR ASIENTOS" }, "manageSeatsDescription": { "message": "Adjustments to seats will be reflected in the next billing cycle." }, "unassignedSeatsDescription": { - "message": "Unassigned seats" + "message": "Asientos no asignados" }, "purchaseSeatDescription": { - "message": "Additional seats purchased" + "message": "Asientos adicionales comprados" }, "assignedSeatCannotUpdate": { "message": "Assigned Seats can not be updated. Please contact your organization owner for assistance." diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 01b64364ea..b82a600afa 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Paroolide ajalugu" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Puuduvad paroolid, mida kuvada." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Tühjenda", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Kõikidest seadmetest on välja logitud" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 158233471d..bd8e1febd5 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Pasahitz historia" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Ez dago erakusteko pasahitzik." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Ezabatu", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Saio guztiei baimena kendua" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 870ffb1f8e..beb39cd47a 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "تاریخچه کلمه عبور" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "هیچ کلمه عبوری برای فهرست کردن وجود ندارد." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "پاک کردن", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "همه نشستها غیرمجاز است" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 95aa99cdb7..4a27d1b5f5 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Salasanahistoria" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Näytettäviä salasanoja ei ole." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Tyhjennä", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Kaikki istunnot mitätöitiin" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Voi hallita organisaation kokoelmien toimintaa." }, - "limitCollectionCreationDeletionDesc": { - "message": "Rajoita kokoelmien luonti ja poisto omistajille ja ylläpitäjille" - }, "limitCollectionCreationDesc": { "message": "Rajoita kokoelmien luonti omistajille ja ylläpitäjille" }, diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 44ecebc6e6..536dbeaebc 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Kasaysayan ng password" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Walang maililistang password." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Burahin", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Na-deauthorize lahat ng mga sesyon" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index fe0e9dc0e5..3bbed3aba4 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Historique des mots de passe" }, + "generatorHistory": { + "message": "Historique du générateur" + }, + "clearGeneratorHistoryTitle": { + "message": "Effacer l'historique du générateur" + }, + "cleargGeneratorHistoryDescription": { + "message": "Si vous continuez, toutes les entrées seront définitivement supprimées de l'historique du générateur. Êtes-vous sûr de vouloir continuer?" + }, "noPasswordsInList": { "message": "Aucun mot de passe à afficher." }, + "clearHistory": { + "message": "Effacer l'historique" + }, + "nothingToShow": { + "message": "Rien à afficher" + }, + "nothingGeneratedRecently": { + "message": "Vous n'avez rien généré récemment" + }, "clear": { "message": "Effacer", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Toutes les sessions ont été révoquées" }, - "accountIsManagedMessage": { - "message": "Ce compte est géré par $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Ce compte appartient à $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Connexion initiée" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Se souvenir de cet appareil pour rendre les connexions futures transparentes" }, "deviceApprovalRequired": { "message": "L'approbation de l'appareil est requise. Sélectionnez une option d'approbation ci-dessous:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Autorisation de l'appareil requise" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Sélectionnez une option d'approbation ci-dessous" }, "rememberThisDevice": { "message": "Se souvenir de cet appareil" @@ -8293,7 +8311,7 @@ "message": "Courriel de l'utilisateur manquant" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Courriel de l'utilisateur actif introuvable. Vous allez être déconnecté." }, "deviceTrusted": { "message": "Appareil de confiance" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Gérer le comportement de la collection pour l'organisation" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limiter la création et la suppression de la collection aux propriétaires et administrateurs" - }, "limitCollectionCreationDesc": { "message": "Limiter la création de collections aux propriétaires et administrateurs" }, diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index a492300c13..ceda4ca59d 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 411ef267e4..9da5dfe492 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "היסטוריית סיסמאות" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "אין סיסמאות להצגה ברשימה." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "נקה", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "הוסרה ההרשאה מכל הסשנים" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 2cd148e301..7c8ecd4473 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 2fc33c3b34..5a5ca9b979 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Povijest" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Nema lozinki na popisu." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Očisti", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Sve sesije deautorizirane" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Upravljaj ponašanjem zbirki za organizaciju" }, - "limitCollectionCreationDeletionDesc": { - "message": "Ograniči kreiranje i brisanje zbirki vlasnicima i adminima" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 2ae6a5f699..7042ac1f6a 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Jelszóelőzmények" }, + "generatorHistory": { + "message": "Generátor előzmények" + }, + "clearGeneratorHistoryTitle": { + "message": "Generátor előzmények kiürítése" + }, + "cleargGeneratorHistoryDescription": { + "message": "Ha folytatjuk, az összes bejegyzés véglegesen törlődik a generátor előzményeiből. Biztosan folytatjuk?" + }, "noPasswordsInList": { "message": "Nincsenek listázható jelszavak." }, + "clearHistory": { + "message": "Előzmények törlése" + }, + "nothingToShow": { + "message": "Nincs megjeleníthető elem" + }, + "nothingGeneratedRecently": { + "message": "Mostanában nem lett semmi generálva." + }, "clear": { "message": "Kiürítés", "description": "To clear something out. Example: To clear browser history." @@ -1767,7 +1785,7 @@ "sessionsDeauthorized": { "message": "Az összes munkamenet hitelesítése eldobásra került." }, - "accountIsManagedMessage": { + "accountIsOwnedMessage": { "message": "Ezt a fiókot $ORGANIZATIONNAME$ kezeli.", "placeholders": { "organizationName": { @@ -8052,16 +8070,16 @@ "message": "A bejelentkezés elindításra került." }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Emlékezés az eszközre, hogy zökkenőmentes legyen a jövőbeni bejelentkezés" }, "deviceApprovalRequired": { "message": "Az eszköz jóváhagyása szükséges. Válasszunk egy jóváhagyási lehetőséget lentebb:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Eszköz jóváhagyás szükséges" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Válasszunk lentebb egy jóváhagyási lehetőséget." }, "rememberThisDevice": { "message": "Eszköz megjegyzése" @@ -8293,7 +8311,7 @@ "message": "A felhasználói email cím hiányzik." }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "" }, "deviceTrusted": { "message": "Az eszköz megbízható." @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Gyűjtési viselkedést kezelhet a szervezetnek" }, - "limitCollectionCreationDeletionDesc": { - "message": "A gyűjtemény létrehozásának és törlésének korlátozása tulajdonosokra és adminisztrátorokra" - }, "limitCollectionCreationDesc": { "message": "A gyűjtemény létrehozásának korlátozása tulajdonosokra és adminisztrátorokra" }, diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 9bc97de8a3..53b7fb2714 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Riwayat Kata Sandi" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Tidak ada sandi yang dapat dicantumkan." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Bersihkan", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Semua Sesi Dicabut Izinnya" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 321e52e4b1..e37992b812 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Cronologia delle password" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Non ci sono password da mostrare." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Cancella", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Tutte le sessioni revocate" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Gestisci il comportamento delle raccolte per l'organizzazione" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limita la creazione e l'eliminazione delle raccolte a proprietari e amministratori" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 429727af20..c212cb6f93 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "パスワードの履歴" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "表示するパスワードがありません" }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "消去する", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "全てのセッションを無効化" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "組織のコレクションに関する挙動を管理します" }, - "limitCollectionCreationDeletionDesc": { - "message": "コレクションの作成・削除を所有者と管理者のみに制限" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index d03ea96816..a3a7d9157e 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 24d2cfb901..1ad5f52a94 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index cc80dfcbd4..1eb0012ccc 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "ಪಾಸ್ವರ್ಡ್ ಇತಿಹಾಸ" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "ಪಟ್ಟಿ ಮಾಡಲು ಯಾವುದೇ ಪಾಸ್ವರ್ಡ್ಗಳು ಇಲ್ಲ." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "ಕ್ಲಿಯರ್", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "ಎಲ್ಲಾ ಸೆಷನ್ಗಳು ಅನಧಿಕೃತವಾಗಿವೆ" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 5ccd3e177a..264e6cf7d3 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "비밀번호 변경 기록" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "비밀번호가 없습니다." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "삭제", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "모든 세션 해제 됨" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index af4908ab00..81d0dde4e6 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Paroles izmaiņu vēsture" }, + "generatorHistory": { + "message": "Veidotāja vēsture" + }, + "clearGeneratorHistoryTitle": { + "message": "Iztīrīt veidotāja vēsturi" + }, + "cleargGeneratorHistoryDescription": { + "message": "Turpinot visi veidotāja vēstures ieraksti tiks neatgrieziniski izdzēsti. Vai tiešām turpināt?" + }, "noPasswordsInList": { "message": "Nav paroļu, ko parādīt." }, + "clearHistory": { + "message": "Iztīrīt vēsturi" + }, + "nothingToShow": { + "message": "Nav nekā, ko parādīt" + }, + "nothingGeneratedRecently": { + "message": "Pēdējā laikā nav nekas izveidots" + }, "clear": { "message": "Notīrīt", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Visu sesiju darbība ir atsaukta" }, - "accountIsManagedMessage": { - "message": "Šo kontu pārvalda $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Šis konts pieder $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -6511,7 +6529,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Jāizmanto $RECOMMENDED$ vai vairāk vārdu, lai aizveidotu spēcīgu parles vārdkopu.", + "message": " Jāizmanto $RECOMMENDED$ vai vairāk vārdu, lai aizveidotu spēcīgu paroles vārdkopu.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -8052,16 +8070,16 @@ "message": "Uzsākta pieteikšanās" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Atcerēties šo ierīci, lai nākotnes pieteikšanos padarītu plūdenāku" }, "deviceApprovalRequired": { "message": "Nepieciešams ierīces apstiprinājums. Zemāk jāatlasa apstiprinājuma iespēja:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Nepieciešama ierīces apstiprināšana" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Zemāk jāatlasa apstiprināšnas iespēja" }, "rememberThisDevice": { "message": "Atcerēties šo ierīci" @@ -8293,7 +8311,7 @@ "message": "Trūkst lietotāja e-pasta adreses" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktīva lietotāja e-pasta adrese netika atrasta. Notiek atteikšanās." }, "deviceTrusted": { "message": "Ierīce ir uzticama" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Krājumu uzvedības pārvaldība apvienībā" }, - "limitCollectionCreationDeletionDesc": { - "message": "Ļaut krājumu izveidošanu un izdzēšanu tikai īpašniekiem un pārvaldniekiem" - }, "limitCollectionCreationDesc": { "message": "Ļaut krājumu izveidošanu tikai īpašniekiem un pārvaldītājiem" }, diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 51d65c719c..559c1e4768 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "പാസ്സ്വേഡ് ചരിത്രം" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "പ്രദർശിപ്പിക്കാൻ പാസ്സ്വേഡുകൾ ഒന്നും ഇല്ല." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "മായ്ക്കുക", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "എല്ലാ സെഷനും നിരസിച്ചു." }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 24d2cfb901..1ad5f52a94 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 24d2cfb901..1ad5f52a94 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 43ba5fea30..5145af4cc1 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Passordhistorikk" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Det er ingen passord å liste opp." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Tøm", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Fjernet autoriseringen fra alle økter" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 079a4217a0..068f477149 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 460fc01add..0f1025abd7 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Geschiedenis" }, + "generatorHistory": { + "message": "Generatorgeschiedenis" + }, + "clearGeneratorHistoryTitle": { + "message": "Generatorgeschiedenis wissen" + }, + "cleargGeneratorHistoryDescription": { + "message": "Als je doorgaat, wis je definitief de geschiedenis van de generator. Weet je zeker dat je wilt doorgaan?" + }, "noPasswordsInList": { "message": "Er zijn geen wachtwoorden om weer te geven." }, + "clearHistory": { + "message": "Geschiedenis wissen" + }, + "nothingToShow": { + "message": "Niets weer te geven" + }, + "nothingGeneratedRecently": { + "message": "Je hebt de laatste tijd niets gegenereerd" + }, "clear": { "message": "Wissen", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Autorisatie van alle sessies ingetrokken" }, - "accountIsManagedMessage": { - "message": "Dit account wordt beheerd door $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Dit account is eigendom van $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Het gedrag van de collectie beheren voor de organisatie" }, - "limitCollectionCreationDeletionDesc": { - "message": "Aanmaken en verwijderen van collecties tot eigenaren en managers beperken" - }, "limitCollectionCreationDesc": { "message": "Aanmaken van collecties beperken tot eigenaren en managers" }, diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index b47839aa8e..eb9058d8d3 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Passordoversyn" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Det er ingen passord å syna." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Tøm", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 24d2cfb901..1ad5f52a94 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 6cabe22b90..e02e23842c 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Historia hasła" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Brak haseł." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Wyczyść", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Wszystkie sesje zostały zakończone" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Zarządzaj zachowaniami kolekcji w organizacji" }, - "limitCollectionCreationDeletionDesc": { - "message": "Ogranicz tworzenie i usuwanie kolekcji dla właścicieli i administratorów" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, @@ -9138,7 +9153,7 @@ } }, "lowKDFIterationsBanner": { - "message": "Niska liczba itracjia KDF. Zwiększ liczbę iteracji, aby zwiększyć bezpieczeństwo Twojego konta." + "message": "Niska liczba iteracji KDF. Zwiększ liczbę iteracji, aby zwiększyć bezpieczeństwo Twojego konta." }, "changeKDFSettings": { "message": "Zmień ustawienia KDF" diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 4f86642b05..5df8556ae6 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Histórico de Senha" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Não existem senhas para listar." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Limpar", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Todas as Sessões Desautorizadas" }, - "accountIsManagedMessage": { - "message": "Esta conta é gerenciada por $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Gerenciar o comportamento da coleção para a organização" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limitar criação de coleção e exclusão a proprietários e administradores" - }, "limitCollectionCreationDesc": { "message": "Limitar criação de coleção e exclusão a proprietários e administradores" }, diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 92e5c0c88e..06d56cb2f7 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Histórico de palavras-passe" }, + "generatorHistory": { + "message": "Histórico do gerador" + }, + "clearGeneratorHistoryTitle": { + "message": "Limpar o histórico do gerador" + }, + "cleargGeneratorHistoryDescription": { + "message": "Se continuar, todas as entradas serão permanentemente eliminadas do histórico do gerador. Tem a certeza de que pretende continuar?" + }, "noPasswordsInList": { "message": "Não existem palavras-passe para listar." }, + "clearHistory": { + "message": "Limpar histórico" + }, + "nothingToShow": { + "message": "Nada a mostrar" + }, + "nothingGeneratedRecently": { + "message": "Não gerou nada recentemente" + }, "clear": { "message": "Limpar", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Todas as sessões desautorizadas" }, - "accountIsManagedMessage": { - "message": "Esta conta é gerida por $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Esta conta é propriedade de $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Gerir o comportamento da coleção da organização" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limitar a criação e eliminação de coleções aos proprietários e administradores" - }, "limitCollectionCreationDesc": { "message": "Limitar a criação de coleções aos proprietários e administradores" }, diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index e8e76e800e..c5c4d7b93a 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Istoric parole" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Nicio parolă de afișat." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Ștergere", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Toate sesiunile au fost dezautorizate" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index e560c4d7be..6d86d11431 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -6,7 +6,7 @@ "message": "Критичные приложения" }, "accessIntelligence": { - "message": "Access Intelligence" + "message": "Управление доступом" }, "riskInsights": { "message": "Информация о рисках" @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "История паролей" }, + "generatorHistory": { + "message": "История генератора" + }, + "clearGeneratorHistoryTitle": { + "message": "Очистить историю генератора" + }, + "cleargGeneratorHistoryDescription": { + "message": "Если вы продолжите, все записи будут навсегда удалены из истории генератора. Вы уверены, что хотите продолжить?" + }, "noPasswordsInList": { "message": "Нет паролей для отображения." }, + "clearHistory": { + "message": "Очистить историю" + }, + "nothingToShow": { + "message": "Нечего показать" + }, + "nothingGeneratedRecently": { + "message": "Вы ничего не создавали в последнее время" + }, "clear": { "message": "Очистить", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Все сессии деавторизованы" }, - "accountIsManagedMessage": { - "message": "Управление этим аккаунтом осуществляет $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Этот аккаунт принадлежит $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Вход инициирован" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Запомнить это устройство, чтобы в будущем авторизовываться быстрее" }, "deviceApprovalRequired": { "message": "Требуется одобрение устройства. Выберите вариант ниже:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Требуется подтверждение устройства" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Выберите вариант подтверждения ниже" }, "rememberThisDevice": { "message": "Запомнить это устройство" @@ -8293,7 +8311,7 @@ "message": "Отсутствует email пользователя" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Email активного пользователя не найден. Разлогиниваем." }, "deviceTrusted": { "message": "Доверенное устройство" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Управление настройками коллекций для организации" }, - "limitCollectionCreationDeletionDesc": { - "message": "Ограничить создание и удаление коллекций владельцам и администраторам" - }, "limitCollectionCreationDesc": { "message": "Ограничить создание коллекций владельцам и администраторам" }, @@ -8430,7 +8445,7 @@ "message": "Максимальная потенциальная стоимость сервисного аккаунта" }, "loggedInExclamation": { - "message": "Вход выполнен!" + "message": "Выполнен вход!" }, "beta": { "message": "Beta" diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index ce6b049315..fd6077f5f1 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 839bc75a87..54b03ab3e4 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "História hesla" }, + "generatorHistory": { + "message": "História generátora" + }, + "clearGeneratorHistoryTitle": { + "message": "Vymazať históriu generátora" + }, + "cleargGeneratorHistoryDescription": { + "message": "Ak budete pokračovať, všetky položky z histórie generátora budu natrvalo vymazané. Naozaj chcete pokračovať?" + }, "noPasswordsInList": { "message": "Neboli nájdené žiadne heslá." }, + "clearHistory": { + "message": "Vymazať históriu" + }, + "nothingToShow": { + "message": "Nie je čo zobraziť" + }, + "nothingGeneratedRecently": { + "message": "V poslednej dobe ste nič negenerovali" + }, "clear": { "message": "Vyčistiť", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Všetky sedenia odhlásené" }, - "accountIsManagedMessage": { - "message": "Tento účet spravuje $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Tento účet vlastní $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Iniciované prihlásenie" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamätať si toto zariadenie, pre budúce bezproblémové prihlásenie" }, "deviceApprovalRequired": { "message": "Vyžaduje sa schválenie zariadenia. Vyberte možnosť schválenia nižšie:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Vyžaduje sa schválenie zariadenia" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Vyberte možnosť schválenia nižšie" }, "rememberThisDevice": { "message": "Zapamätať si toto zariadenie" @@ -8293,7 +8311,7 @@ "message": "Chýba e-mail používateľa" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "E-mail aktívneho používateľa sa nenašiel. Odhlasuje sa." }, "deviceTrusted": { "message": "Dôveryhodné zariadenie" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Spravovať správanie zbierky pre organizáciu" }, - "limitCollectionCreationDeletionDesc": { - "message": "Obmedziť vytváranie a vymazávanie zbierky len pre vlastníkov a administrátorov" - }, "limitCollectionCreationDesc": { "message": "Obmedziť vytváranie zbierky len pre vlastníkov a administrátorov" }, diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 9258380508..07f6dc1b91 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Zgodovina gesel" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Ni gesel za prikaz." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Počisti", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Vse seje prekinjene" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index 7966b20f89..6d2b8a18cf 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Историја Лозинке" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Нама лозинке у листи." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Очисти", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Одузето овлашћење свих сесија" }, - "accountIsManagedMessage": { - "message": "Овим налогом управља $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Управљајте понашањем збирке за организацију" }, - "limitCollectionCreationDeletionDesc": { - "message": "Ограничите креирање и брисање збирке на власнике и администраторе" - }, "limitCollectionCreationDesc": { "message": "Ограничите креирање збирке на власнике и администраторе" }, diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index fa98bbe6d5..4a9125537c 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Istorija lozinki" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Očisti", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 16ee1126c0..bd67758aaa 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Lösenordshistorik" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Det finns inga lösenord att visa." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Rensa", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Alla sessioner avauktoriserades" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 24d2cfb901..1ad5f52a94 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index bb0351a930..6744d955ca 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Password history" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "There are no passwords to list." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Clear", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "All sessions deauthorized" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index d8741205c1..e83bd6667d 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Parola geçmişi" }, + "generatorHistory": { + "message": "Üreteç geçmişi" + }, + "clearGeneratorHistoryTitle": { + "message": "Üreteç geçmişini temizle" + }, + "cleargGeneratorHistoryDescription": { + "message": "Devam ederseniz üreteç geçmişindeki tüm kayıtlar kalıcı olarak silinecektir. Devam etmek istediğinizden emin misiniz?" + }, "noPasswordsInList": { "message": "Listelenecek parola yok." }, + "clearHistory": { + "message": "Geçmişi temizle" + }, + "nothingToShow": { + "message": "Gösterilecek bir şey yok" + }, + "nothingGeneratedRecently": { + "message": "Yakın zamanda herhangi bir şey üretmediniz" + }, "clear": { "message": "Temizle", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Tüm oturumlar kapatıldı" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "Bu hesap $ORGANIZATIONNAME$ kuruluşuna aittir", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "Giriş başlatıldı" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Sonraki girişleri kolaylaştırmak için bu cihazı hatırla" }, "deviceApprovalRequired": { "message": "Cihaz onayı gerekiyor. Lütfen onay yönteminizi seçin:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Cihazı onaylamanız gerekiyor" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Aşağıdan bir onay yöntemi seçin" }, "rememberThisDevice": { "message": "Bu cihazı hatırla" @@ -8293,7 +8311,7 @@ "message": "Kullanıcının e-postası eksik" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Aktif kullanıcı e-postası bulunamadı. Çıkış yapılıyor." }, "deviceTrusted": { "message": "Cihaza güvenildi" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Kuruluş için toplama davranışını yönetin" }, - "limitCollectionCreationDeletionDesc": { - "message": "Koleksiyon oluşturma ve silme işlemlerini sahipler ve yöneticilerle sınırlandırın" - }, "limitCollectionCreationDesc": { "message": "Koleksiyon oluşturmayı sahipler ve yöneticilerle sınırlandırın" }, diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 2a438a711d..bde7ab4dbe 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Історія паролів" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Немає паролів." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Стерти", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Усі сеанси завершено" }, - "accountIsManagedMessage": { - "message": "Цим обліковим записом керує $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Керувати налаштуваннями збірок для організації" }, - "limitCollectionCreationDeletionDesc": { - "message": "Дозволити створення та видалення збірок лише власникам та адміністраторам" - }, "limitCollectionCreationDesc": { "message": "Дозволити створення збірок лише власникам та адміністраторам" }, diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index cccc634d9e..d6701a713e 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "Lịch sử mật khẩu" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "Chưa có mật khẩu." }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "Xóa", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "Tất cả phiên đăng nhập đã bị gỡ" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "Manage the collection behavior for the organization" }, - "limitCollectionCreationDeletionDesc": { - "message": "Limit collection creation and deletion to owners and admins" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 188c464314..c3b1c6a5ef 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "密码历史记录" }, + "generatorHistory": { + "message": "生成器历史记录" + }, + "clearGeneratorHistoryTitle": { + "message": "清除生成器历史记录" + }, + "cleargGeneratorHistoryDescription": { + "message": "若继续,所有条目将从生成器历史记录中永久删除。确定要继续吗?" + }, "noPasswordsInList": { "message": "没有可列出的密码。" }, + "clearHistory": { + "message": "清除历史记录" + }, + "nothingToShow": { + "message": "没有可显示的内容" + }, + "nothingGeneratedRecently": { + "message": "您最近没有生成任何内容" + }, "clear": { "message": "清除", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "已取消所有会话授权" }, - "accountIsManagedMessage": { - "message": "此账户由 $ORGANIZATIONNAME$ 管理", + "accountIsOwnedMessage": { + "message": "此账户由 $ORGANIZATIONNAME$ 拥有", "placeholders": { "organizationName": { "content": "$1", @@ -8052,16 +8070,16 @@ "message": "登录已发起" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "记住此设备以便将来无缝登录" }, "deviceApprovalRequired": { "message": "需要设备批准。请在下面选择一个批准选项:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "需要设备批准" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "在下方选择一个批准选项" }, "rememberThisDevice": { "message": "记住此设备" @@ -8293,7 +8311,7 @@ "message": "缺少用户电子邮件" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "未找到活动的用户电子邮件。您将被注销。" }, "deviceTrusted": { "message": "设备已信任" @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "管理组织的集合行为" }, - "limitCollectionCreationDeletionDesc": { - "message": "限制为仅所有者和管理员可以创建和删除集合" - }, "limitCollectionCreationDesc": { "message": "限制为仅所有者和管理员可以创建集合" }, @@ -9759,18 +9774,18 @@ "message": "删除成功" }, "freeFamiliesSponsorship": { - "message": "删除免费的 Bitwarden 家庭赞助" + "message": "移除免费的 Bitwarden 家庭赞助" }, "freeFamiliesSponsorshipPolicyDesc": { "message": "不允许成员通过此组织兑换家庭计划。" }, "verifyBankAccountWithStatementDescriptorWarning": { - "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the organization's billing page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "使用银行账户付款仅对美国用户开放。您将被要求验证您的银行账户。我们将在 1-2 个工作日内进行一笔小额转账,请在组织的计费页面输入该转账的语句描述符代码以验证银行账户。验证银行账户失败将会错过支付,您的订阅将失效。" }, "verifyBankAccountWithStatementDescriptorInstructions": { - "message": "We have made a micro-deposit to your bank account (this may take 1-2 business days). Enter the six-digit code starting with 'SM' found on the deposit description. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "我们已向您的银行账户存入了一笔小额转账(可能需要 1-2 个工作日到账)。请输入转账说明中以 \"SM\" 开头的六位数代码。验证银行账户失败将会错过支付,您的订阅将失效。" }, "descriptorCode": { - "message": "Descriptor code" + "message": "描述符代码" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index d1a83596a2..2d39d982fc 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -1642,9 +1642,27 @@ "passwordHistory": { "message": "密碼歷史記錄" }, + "generatorHistory": { + "message": "Generator history" + }, + "clearGeneratorHistoryTitle": { + "message": "Clear generator history" + }, + "cleargGeneratorHistoryDescription": { + "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + }, "noPasswordsInList": { "message": "沒有可列出的密碼。" }, + "clearHistory": { + "message": "Clear history" + }, + "nothingToShow": { + "message": "Nothing to show" + }, + "nothingGeneratedRecently": { + "message": "You haven't generated anything recently" + }, "clear": { "message": "清除", "description": "To clear something out. Example: To clear browser history." @@ -1767,8 +1785,8 @@ "sessionsDeauthorized": { "message": "已取消所有工作階段授權" }, - "accountIsManagedMessage": { - "message": "This account is managed by $ORGANIZATIONNAME$", + "accountIsOwnedMessage": { + "message": "This account is owned by $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -8390,9 +8408,6 @@ "collectionManagementDesc": { "message": "管理組織分類的行為" }, - "limitCollectionCreationDeletionDesc": { - "message": "對擁有者和管理員限制集合的建立和刪除" - }, "limitCollectionCreationDesc": { "message": "Limit collection creation to owners and admins" }, diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts index 987888741a..9c0bae1052 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts @@ -14,6 +14,8 @@ import { import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction"; import { OrgDomainServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain.service.abstraction"; import { OrganizationDomainResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain.response"; +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { HttpStatusCode } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; @@ -33,6 +35,7 @@ import { }) export class DomainVerificationComponent implements OnInit, OnDestroy { private componentDestroyed$ = new Subject(); + private singleOrgPolicyEnabled = false; loading = true; @@ -48,6 +51,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { private validationService: ValidationService, private toastService: ToastService, private configService: ConfigService, + private policyApiService: PolicyApiServiceAbstraction, ) {} // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -71,6 +75,14 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { async load() { await this.orgDomainApiService.getAllByOrgId(this.organizationId); + if (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) { + const singleOrgPolicy = await this.policyApiService.getPolicy( + this.organizationId, + PolicyType.SingleOrg, + ); + this.singleOrgPolicyEnabled = singleOrgPolicy?.enabled ?? false; + } + this.loading = false; } @@ -87,6 +99,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { map(async ([accountDeprovisioningEnabled, organizationDomains]) => { if ( accountDeprovisioningEnabled && + !this.singleOrgPolicyEnabled && organizationDomains.every((domain) => domain.verifiedDate === null) ) { await this.dialogService.openSimpleDialog({ diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations-routing.module.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations-routing.module.ts index 8e5860833c..3a76b92ced 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations-routing.module.ts @@ -76,6 +76,13 @@ const routes: Routes = [ }, ], }, + { + path: "access-intelligence", + loadChildren: () => + import("../../tools/access-intelligence/access-intelligence.module").then( + (m) => m.AccessIntelligenceModule, + ), + }, ], }, ]; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts index a4461b3e11..68264593b8 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts @@ -1,4 +1,5 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, OnInit, SecurityContext } from "@angular/core"; +import { DomSanitizer } from "@angular/platform-browser"; import { ActivatedRoute, Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; @@ -24,6 +25,7 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit { private i18nService: I18nService, private route: ActivatedRoute, private toastService: ToastService, + private sanitizer: DomSanitizer, ) {} async ngOnInit() { @@ -31,7 +33,10 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit { if (qParams.providerId != null && qParams.token != null && qParams.name != null) { this.providerId = qParams.providerId; this.token = qParams.token; - this.name = qParams.name; + this.name = + qParams.name && typeof qParams.name === "string" + ? this.sanitizer.sanitize(SecurityContext.HTML, qParams.name) || "" + : ""; } else { await this.router.navigate(["/"]); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts index bf2dbb76ad..3585f09faf 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts @@ -20,6 +20,7 @@ import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions"; 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"; @@ -114,9 +115,9 @@ export class OverviewComponent implements OnInit, OnDestroy { private smOnboardingTasksService: SMOnboardingTasksService, private logService: LogService, private router: Router, - private organizationApiService: OrganizationApiServiceAbstraction, private trialFlowService: TrialFlowService, + private organizationBillingService: OrganizationBillingServiceAbstraction, ) {} ngOnInit() { @@ -144,15 +145,11 @@ export class OverviewComponent implements OnInit, OnDestroy { combineLatest([ of(org), this.organizationApiService.getSubscription(org.id), - this.organizationApiService.getBilling(org.id), + this.organizationBillingService.getPaymentSource(org.id), ]), ), - map(([org, sub, billing]) => { - return this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( - org, - sub, - billing?.paymentSource, - ); + map(([org, sub, paymentSource]) => { + return this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(org, sub, paymentSource); }), takeUntil(this.destroy$), ); diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence-routing.module.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence-routing.module.ts similarity index 100% rename from apps/web/src/app/tools/access-intelligence/access-intelligence-routing.module.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence-routing.module.ts diff --git a/apps/web/src/app/tools/access-intelligence/access-intelligence.module.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence.module.ts similarity index 100% rename from apps/web/src/app/tools/access-intelligence/access-intelligence.module.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/access-intelligence.module.ts diff --git a/apps/web/src/app/tools/access-intelligence/all-applications.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.html similarity index 100% rename from apps/web/src/app/tools/access-intelligence/all-applications.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.html diff --git a/apps/web/src/app/tools/access-intelligence/all-applications.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts similarity index 94% rename from apps/web/src/app/tools/access-intelligence/all-applications.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts index 5d76403f46..c755aa7259 100644 --- a/apps/web/src/app/tools/access-intelligence/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts @@ -21,10 +21,9 @@ import { ToastService, } from "@bitwarden/components"; import { CardComponent } from "@bitwarden/tools-card"; - -import { HeaderModule } from "../../layouts/header/header.module"; -import { SharedModule } from "../../shared"; -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { applicationTableMockData } from "./application-table.mock"; diff --git a/apps/web/src/app/tools/access-intelligence/application-table.mock.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/application-table.mock.ts similarity index 100% rename from apps/web/src/app/tools/access-intelligence/application-table.mock.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/application-table.mock.ts diff --git a/apps/web/src/app/tools/access-intelligence/critical-applications.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html similarity index 100% rename from apps/web/src/app/tools/access-intelligence/critical-applications.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html diff --git a/apps/web/src/app/tools/access-intelligence/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts similarity index 90% rename from apps/web/src/app/tools/access-intelligence/critical-applications.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts index 0779b2977e..43ff3d458b 100644 --- a/apps/web/src/app/tools/access-intelligence/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts @@ -7,10 +7,9 @@ import { debounceTime, map } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SearchModule, TableDataSource, NoItemsModule, Icons } from "@bitwarden/components"; import { CardComponent } from "@bitwarden/tools-card"; - -import { HeaderModule } from "../../layouts/header/header.module"; -import { SharedModule } from "../../shared"; -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { applicationTableMockData } from "./application-table.mock"; import { RiskInsightsTabType } from "./risk-insights.component"; diff --git a/apps/web/src/app/tools/access-intelligence/notified-members-table.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/notified-members-table.component.html similarity index 100% rename from apps/web/src/app/tools/access-intelligence/notified-members-table.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/notified-members-table.component.html diff --git a/apps/web/src/app/tools/access-intelligence/notified-members-table.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/notified-members-table.component.ts similarity index 100% rename from apps/web/src/app/tools/access-intelligence/notified-members-table.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/notified-members-table.component.ts diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.html similarity index 100% rename from apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.html diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.spec.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.spec.ts similarity index 93% rename from apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.spec.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.spec.ts index e3011604a4..1e9e4171bc 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.spec.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.spec.ts @@ -3,7 +3,6 @@ import { ActivatedRoute, convertToParamMap } from "@angular/router"; import { mock, MockProxy } from "jest-mock-extended"; import { of } from "rxjs"; -// eslint-disable-next-line no-restricted-imports import { MemberCipherDetailsApiService, PasswordHealthService, @@ -15,9 +14,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { TableModule } from "@bitwarden/components"; - -import { LooseComponentsModule } from "../../shared"; -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { PasswordHealthMembersURIComponent } from "./password-health-members-uri.component"; diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.ts similarity index 92% rename from apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.ts index c8aea97ef7..ffc9a7911e 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health-members-uri.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members-uri.component.ts @@ -5,7 +5,6 @@ import { ActivatedRoute } from "@angular/router"; import { map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -// eslint-disable-next-line no-restricted-imports import { MemberCipherDetailsApiService, PasswordHealthService, @@ -24,11 +23,8 @@ import { TableDataSource, TableModule, } from "@bitwarden/components"; - -// eslint-disable-next-line no-restricted-imports -import { HeaderModule } from "../../layouts/header/header.module"; -// eslint-disable-next-line no-restricted-imports -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @Component({ standalone: true, diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members.component.html similarity index 100% rename from apps/web/src/app/tools/access-intelligence/password-health-members.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members.component.html diff --git a/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members.component.ts similarity index 92% rename from apps/web/src/app/tools/access-intelligence/password-health-members.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members.component.ts index 66ff348e9f..19eded0de6 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health-members.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health-members.component.ts @@ -4,7 +4,6 @@ import { FormControl, FormsModule } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; import { debounceTime, map } from "rxjs"; -// eslint-disable-next-line no-restricted-imports import { MemberCipherDetailsApiService, PasswordHealthService, @@ -21,12 +20,9 @@ import { TableModule, ToastService, } from "@bitwarden/components"; - -import { HeaderModule } from "../../layouts/header/header.module"; -// eslint-disable-next-line no-restricted-imports -import { SharedModule } from "../../shared"; -// eslint-disable-next-line no-restricted-imports -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @Component({ standalone: true, diff --git a/apps/web/src/app/tools/access-intelligence/password-health.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.html similarity index 100% rename from apps/web/src/app/tools/access-intelligence/password-health.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.html diff --git a/apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts similarity index 93% rename from apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts index 5e934e3edf..98637d0dec 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health.component.spec.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts @@ -3,7 +3,6 @@ import { ActivatedRoute, convertToParamMap } from "@angular/router"; import { mock } from "jest-mock-extended"; import { of } from "rxjs"; -// eslint-disable-next-line no-restricted-imports import { MemberCipherDetailsApiService, PasswordHealthService, @@ -15,9 +14,8 @@ import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/pass import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { TableModule } from "@bitwarden/components"; import { TableBodyDirective } from "@bitwarden/components/src/table/table.component"; - -import { LooseComponentsModule } from "../../shared"; -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { PasswordHealthComponent } from "./password-health.component"; diff --git a/apps/web/src/app/tools/access-intelligence/password-health.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts similarity index 86% rename from apps/web/src/app/tools/access-intelligence/password-health.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts index 058cfb86da..8ae14562f3 100644 --- a/apps/web/src/app/tools/access-intelligence/password-health.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts @@ -5,7 +5,6 @@ import { ActivatedRoute } from "@angular/router"; import { map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -// eslint-disable-next-line no-restricted-imports import { MemberCipherDetailsApiService, PasswordHealthService, @@ -22,13 +21,9 @@ import { TableDataSource, TableModule, } from "@bitwarden/components"; - -// eslint-disable-next-line no-restricted-imports -import { HeaderModule } from "../../layouts/header/header.module"; -// eslint-disable-next-line no-restricted-imports -import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module"; -// eslint-disable-next-line no-restricted-imports -import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; +import { OrganizationBadgeModule } from "@bitwarden/web-vault/app/vault/individual-vault/organization-badge/organization-badge.module"; +import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @Component({ standalone: true, diff --git a/apps/web/src/app/tools/access-intelligence/risk-insights.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html similarity index 95% rename from apps/web/src/app/tools/access-intelligence/risk-insights.component.html rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html index 067207160d..6df47e3c46 100644 --- a/apps/web/src/app/tools/access-intelligence/risk-insights.component.html +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html @@ -4,7 +4,7 @@ {{ "reviewAtRiskPasswords" | i18n }} {{ "learnMore" | i18n }} - + {{ "dataLastUpdated" | i18n: (dataLastUpdated | date: "MMMM d, y 'at' h:mm a") diff --git a/apps/web/src/app/tools/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts similarity index 96% rename from apps/web/src/app/tools/access-intelligence/risk-insights.component.ts rename to bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts index 1c6a36b445..2308de2873 100644 --- a/apps/web/src/app/tools/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts @@ -7,8 +7,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { AsyncActionsModule, ButtonModule, TabsModule } from "@bitwarden/components"; - -import { HeaderModule } from "../../layouts/header/header.module"; +import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { AllApplicationsComponent } from "./all-applications.component"; import { CriticalApplicationsComponent } from "./critical-applications.component"; diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-expired.component.ts b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-expired.component.ts new file mode 100644 index 0000000000..faa08cf073 --- /dev/null +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth-expired.component.ts @@ -0,0 +1,25 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ButtonModule } from "@bitwarden/components"; + +/** + * This component is used to display a message to the user that their authentication session has expired. + * It provides a button to navigate to the login page. + */ +@Component({ + selector: "app-two-factor-expired", + standalone: true, + imports: [CommonModule, JslibModule, ButtonModule, RouterModule], + template: ` + + {{ "authenticationSessionTimedOut" | i18n }} + + + {{ "logIn" | i18n }} + + `, +}) +export class TwoFactorTimeoutComponent {} diff --git a/libs/angular/src/auth/components/two-factor-v1.component.spec.ts b/libs/angular/src/auth/components/two-factor-v1.component.spec.ts index 55be60aafd..10d227c2fe 100644 --- a/libs/angular/src/auth/components/two-factor-v1.component.spec.ts +++ b/libs/angular/src/auth/components/two-factor-v1.component.spec.ts @@ -86,9 +86,12 @@ describe("TwoFactorComponent", () => { }; let selectedUserDecryptionOptions: BehaviorSubject; + let twoFactorTimeoutSubject: BehaviorSubject; beforeEach(() => { + twoFactorTimeoutSubject = new BehaviorSubject(false); mockLoginStrategyService = mock(); + mockLoginStrategyService.twoFactorTimeout$ = twoFactorTimeoutSubject; mockRouter = mock(); mockI18nService = mock(); mockApiService = mock(); @@ -492,4 +495,10 @@ describe("TwoFactorComponent", () => { }); }); }); + + it("navigates to the timeout route when timeout expires", async () => { + twoFactorTimeoutSubject.next(true); + + expect(mockRouter.navigate).toHaveBeenCalledWith(["2fa-timeout"]); + }); }); diff --git a/libs/angular/src/auth/components/two-factor-v1.component.ts b/libs/angular/src/auth/components/two-factor-v1.component.ts index 70de10c19c..1717397164 100644 --- a/libs/angular/src/auth/components/two-factor-v1.component.ts +++ b/libs/angular/src/auth/components/two-factor-v1.component.ts @@ -1,4 +1,5 @@ -import { Directive, Inject, OnDestroy, OnInit } from "@angular/core"; +import { Directive, Inject, OnInit, OnDestroy } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, NavigationExtras, Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; import { first } from "rxjs/operators"; @@ -68,6 +69,7 @@ export class TwoFactorComponentV1 extends CaptchaProtectedComponent implements O protected changePasswordRoute = "set-password"; protected forcePasswordResetRoute = "update-temp-password"; protected successRoute = "vault"; + protected twoFactorTimeoutRoute = "2fa-timeout"; get isDuoProvider(): boolean { return ( @@ -99,6 +101,21 @@ export class TwoFactorComponentV1 extends CaptchaProtectedComponent implements O ) { super(environmentService, i18nService, platformUtilsService, toastService); this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win); + + // Add subscription to twoFactorTimeout$ and navigate to twoFactorTimeoutRoute if expired + this.loginStrategyService.twoFactorTimeout$ + .pipe(takeUntilDestroyed()) + .subscribe(async (expired) => { + if (!expired) { + return; + } + + try { + await this.router.navigate([this.twoFactorTimeoutRoute]); + } catch (err) { + this.logService.error(`Failed to navigate to ${this.twoFactorTimeoutRoute} route`, err); + } + }); } async ngOnInit() { diff --git a/libs/angular/src/scss/bwicons/fonts/bwi-font.svg b/libs/angular/src/scss/bwicons/fonts/bwi-font.svg index 606f39e116..c8535bebef 100644 --- a/libs/angular/src/scss/bwicons/fonts/bwi-font.svg +++ b/libs/angular/src/scss/bwicons/fonts/bwi-font.svg @@ -119,7 +119,7 @@ - + diff --git a/libs/angular/src/scss/bwicons/fonts/bwi-font.ttf b/libs/angular/src/scss/bwicons/fonts/bwi-font.ttf index 523c5233e0..9696152a49 100644 Binary files a/libs/angular/src/scss/bwicons/fonts/bwi-font.ttf and b/libs/angular/src/scss/bwicons/fonts/bwi-font.ttf differ diff --git a/libs/angular/src/scss/bwicons/fonts/bwi-font.woff b/libs/angular/src/scss/bwicons/fonts/bwi-font.woff index 4eef2c8603..554375a1ce 100644 Binary files a/libs/angular/src/scss/bwicons/fonts/bwi-font.woff and b/libs/angular/src/scss/bwicons/fonts/bwi-font.woff differ diff --git a/libs/angular/src/scss/bwicons/fonts/bwi-font.woff2 b/libs/angular/src/scss/bwicons/fonts/bwi-font.woff2 index 7353bb99ef..80544871f3 100644 Binary files a/libs/angular/src/scss/bwicons/fonts/bwi-font.woff2 and b/libs/angular/src/scss/bwicons/fonts/bwi-font.woff2 differ diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 0208a3cdc7..a43f1fa07a 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1213,6 +1213,8 @@ const safeProviders: SafeProvider[] = [ useClass: OrganizationBillingService, deps: [ ApiServiceAbstraction, + BillingApiServiceAbstraction, + ConfigService, KeyServiceAbstraction, EncryptService, I18nServiceAbstraction, diff --git a/libs/auth/src/angular/icons/index.ts b/libs/auth/src/angular/icons/index.ts index 9c444df570..05bb630fcb 100644 --- a/libs/auth/src/angular/icons/index.ts +++ b/libs/auth/src/angular/icons/index.ts @@ -10,3 +10,4 @@ export * from "./vault.icon"; export * from "./registration-user-add.icon"; export * from "./registration-lock-alt.icon"; export * from "./registration-expired-link.icon"; +export * from "./two-factor-timeout.icon"; diff --git a/libs/auth/src/angular/icons/two-factor-timeout.icon.ts b/libs/auth/src/angular/icons/two-factor-timeout.icon.ts new file mode 100644 index 0000000000..71d0aa549d --- /dev/null +++ b/libs/auth/src/angular/icons/two-factor-timeout.icon.ts @@ -0,0 +1,8 @@ +import { svgIcon } from "@bitwarden/components"; + +export const TwoFactorTimeoutIcon = svgIcon` + + + + +`; diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index 16ae77e937..a01b8849c8 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -66,3 +66,7 @@ export * from "./vault-timeout-input/vault-timeout-input.component"; // self hosted environment configuration dialog export * from "./self-hosted-env-config-dialog/self-hosted-env-config-dialog.component"; + +// login approval +export * from "./login-approval/login-approval.component"; +export * from "./login-approval/default-login-approval-component.service"; diff --git a/libs/auth/src/angular/lock/lock.component.ts b/libs/auth/src/angular/lock/lock.component.ts index 94c226f3f6..d8a2853a04 100644 --- a/libs/auth/src/angular/lock/lock.component.ts +++ b/libs/auth/src/angular/lock/lock.component.ts @@ -228,6 +228,7 @@ export class LockV2Component implements OnInit, OnDestroy { this.unlockOptions = null; this.activeUnlockOption = null; this.formGroup = null; // new form group will be created based on new active unlock option + this.isInitialLockScreen = true; // Desktop properties: this.biometricAsked = false; diff --git a/libs/auth/src/angular/login-approval/default-login-approval-component.service.spec.ts b/libs/auth/src/angular/login-approval/default-login-approval-component.service.spec.ts new file mode 100644 index 0000000000..ec274fac8b --- /dev/null +++ b/libs/auth/src/angular/login-approval/default-login-approval-component.service.spec.ts @@ -0,0 +1,25 @@ +import { TestBed } from "@angular/core/testing"; + +import { DefaultLoginApprovalComponentService } from "./default-login-approval-component.service"; +import { LoginApprovalComponent } from "./login-approval.component"; + +describe("DefaultLoginApprovalComponentService", () => { + let service: DefaultLoginApprovalComponentService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [DefaultLoginApprovalComponentService], + }); + + service = TestBed.inject(DefaultLoginApprovalComponentService); + }); + + it("is created successfully", () => { + expect(service).toBeTruthy(); + }); + + it("has showLoginRequestedAlertIfWindowNotVisible method that is a no-op", async () => { + const loginApprovalComponent = {} as LoginApprovalComponent; + await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalComponent.email); + }); +}); diff --git a/libs/auth/src/angular/login-approval/default-login-approval-component.service.ts b/libs/auth/src/angular/login-approval/default-login-approval-component.service.ts new file mode 100644 index 0000000000..8b0463be6c --- /dev/null +++ b/libs/auth/src/angular/login-approval/default-login-approval-component.service.ts @@ -0,0 +1,16 @@ +import { LoginApprovalComponentServiceAbstraction } from "../../common/abstractions/login-approval-component.service.abstraction"; + +/** + * Default implementation of the LoginApprovalComponentServiceAbstraction. + */ +export class DefaultLoginApprovalComponentService + implements LoginApprovalComponentServiceAbstraction +{ + /** + * No-op implementation of the showLoginRequestedAlertIfWindowNotVisible method. + * @returns + */ + async showLoginRequestedAlertIfWindowNotVisible(email?: string): Promise { + return; + } +} diff --git a/apps/desktop/src/auth/login/login-approval.component.html b/libs/auth/src/angular/login-approval/login-approval.component.html similarity index 93% rename from apps/desktop/src/auth/login/login-approval.component.html rename to libs/auth/src/angular/login-approval/login-approval.component.html index cc2c0536c9..ddbc48d71a 100644 --- a/apps/desktop/src/auth/login/login-approval.component.html +++ b/libs/auth/src/angular/login-approval/login-approval.component.html @@ -1,7 +1,7 @@ {{ "areYouTryingtoLogin" | i18n }} - {{ "logInAttemptBy" | i18n: email }} + {{ "logInAttemptBy" | i18n: email }} {{ "fingerprintPhraseHeader" | i18n }} {{ fingerprintPhrase }} diff --git a/libs/auth/src/angular/login-approval/login-approval.component.spec.ts b/libs/auth/src/angular/login-approval/login-approval.component.spec.ts new file mode 100644 index 0000000000..ff598bdeb9 --- /dev/null +++ b/libs/auth/src/angular/login-approval/login-approval.component.spec.ts @@ -0,0 +1,122 @@ +import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { + AuthRequestServiceAbstraction, + LoginApprovalComponentServiceAbstraction, +} from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { ToastService } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; + +import { LoginApprovalComponent } from "./login-approval.component"; + +describe("LoginApprovalComponent", () => { + let component: LoginApprovalComponent; + let fixture: ComponentFixture; + + let authRequestService: MockProxy; + let accountService: MockProxy; + let apiService: MockProxy; + let i18nService: MockProxy; + let dialogRef: MockProxy; + let toastService: MockProxy; + + const testNotificationId = "test-notification-id"; + const testEmail = "test@bitwarden.com"; + const testPublicKey = "test-public-key"; + + beforeEach(async () => { + authRequestService = mock(); + accountService = mock(); + apiService = mock(); + i18nService = mock(); + dialogRef = mock(); + toastService = mock(); + + accountService.activeAccount$ = of({ + email: testEmail, + id: "test-user-id" as UserId, + emailVerified: true, + name: null, + }); + + await TestBed.configureTestingModule({ + imports: [LoginApprovalComponent], + providers: [ + { provide: DIALOG_DATA, useValue: { notificationId: testNotificationId } }, + { provide: AuthRequestServiceAbstraction, useValue: authRequestService }, + { provide: AccountService, useValue: accountService }, + { provide: PlatformUtilsService, useValue: mock() }, + { provide: I18nService, useValue: i18nService }, + { provide: ApiService, useValue: apiService }, + { provide: AppIdService, useValue: mock() }, + { provide: KeyService, useValue: mock() }, + { provide: DialogRef, useValue: dialogRef }, + { provide: ToastService, useValue: toastService }, + { + provide: LoginApprovalComponentServiceAbstraction, + useValue: mock(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(LoginApprovalComponent); + component = fixture.componentInstance; + }); + + it("creates successfully", () => { + expect(component).toBeTruthy(); + }); + + describe("ngOnInit", () => { + beforeEach(() => { + apiService.getAuthRequest.mockResolvedValue({ + publicKey: testPublicKey, + creationDate: new Date().toISOString(), + } as AuthRequestResponse); + authRequestService.getFingerprintPhrase.mockResolvedValue("test-phrase"); + }); + + it("retrieves and sets auth request data", async () => { + await component.ngOnInit(); + + expect(apiService.getAuthRequest).toHaveBeenCalledWith(testNotificationId); + expect(component.email).toBe(testEmail); + expect(component.fingerprintPhrase).toBeDefined(); + }); + + it("updates time text initially", async () => { + i18nService.t.mockReturnValue("justNow"); + + await component.ngOnInit(); + expect(component.requestTimeText).toBe("justNow"); + }); + }); + + describe("denyLogin", () => { + it("denies auth request and shows info toast", async () => { + const response = { requestApproved: false } as AuthRequestResponse; + apiService.getAuthRequest.mockResolvedValue(response); + authRequestService.approveOrDenyAuthRequest.mockResolvedValue(response); + i18nService.t.mockReturnValue("denied message"); + + await component.denyLogin(); + + expect(authRequestService.approveOrDenyAuthRequest).toHaveBeenCalledWith(false, response); + expect(toastService.showToast).toHaveBeenCalledWith({ + variant: "info", + title: null, + message: "denied message", + }); + }); + }); +}); diff --git a/apps/desktop/src/auth/login/login-approval.component.ts b/libs/auth/src/angular/login-approval/login-approval.component.ts similarity index 94% rename from apps/desktop/src/auth/login/login-approval.component.ts rename to libs/auth/src/angular/login-approval/login-approval.component.ts index e6428e0020..9dff4d3e27 100644 --- a/apps/desktop/src/auth/login/login-approval.component.ts +++ b/libs/auth/src/angular/login-approval/login-approval.component.ts @@ -4,7 +4,10 @@ import { Component, OnInit, OnDestroy, Inject } from "@angular/core"; import { Subject, firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; +import { + AuthRequestServiceAbstraction, + LoginApprovalComponentServiceAbstraction as LoginApprovalComponentService, +} from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; @@ -56,6 +59,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { protected keyService: KeyService, private dialogRef: DialogRef, private toastService: ToastService, + private loginApprovalComponentService: LoginApprovalComponentService, ) { this.notificationId = params.notificationId; } @@ -89,14 +93,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { this.updateTimeText(); }, RequestTimeUpdate); - const isVisible = await ipc.platform.isWindowVisible(); - if (!isVisible) { - await ipc.auth.loginRequest( - this.i18nService.t("logInRequested"), - this.i18nService.t("confirmLoginAtemptForMail", this.email), - this.i18nService.t("close"), - ); - } + this.loginApprovalComponentService.showLoginRequestedAlertIfWindowNotVisible(this.email); } } diff --git a/libs/auth/src/angular/login/login.component.html b/libs/auth/src/angular/login/login.component.html index 0629e70a6a..efea291752 100644 --- a/libs/auth/src/angular/login/login.component.html +++ b/libs/auth/src/angular/login/login.component.html @@ -97,14 +97,6 @@ {{ "getMasterPasswordHint" | i18n }} - - - diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 9f86de8372..13dec24b60 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, ElementRef, Input, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms"; import { ActivatedRoute, Router, RouterModule } from "@angular/router"; import { firstValueFrom, Subject, take, takeUntil, tap } from "rxjs"; @@ -15,7 +15,6 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; -import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { ClientType, HttpStatusCode } from "@bitwarden/common/enums"; @@ -24,7 +23,6 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -73,14 +71,11 @@ export enum LoginUiState { }) export class LoginComponent implements OnInit, OnDestroy { @ViewChild("masterPasswordInputRef") masterPasswordInputRef: ElementRef; - @Input() captchaSiteKey: string = null; private destroy$ = new Subject(); private enforcedMasterPasswordOptions: MasterPasswordPolicyOptions = undefined; readonly Icons = { WaveIcon, VaultIcon }; - captcha: CaptchaIFrame; - captchaToken: string = null; clientType: ClientType; ClientType = ClientType; LoginUiState = LoginUiState; @@ -118,7 +113,6 @@ export class LoginComponent implements OnInit, OnDestroy { private appIdService: AppIdService, private broadcasterService: BroadcasterService, private devicesApiService: DevicesApiServiceAbstraction, - private environmentService: EnvironmentService, private formBuilder: FormBuilder, private i18nService: I18nService, private loginEmailService: LoginEmailServiceAbstraction, @@ -193,8 +187,6 @@ export class LoginComponent implements OnInit, OnDestroy { const { email, masterPassword } = this.formGroup.value; - await this.setupCaptcha(); - this.formGroup.markAllAsTouched(); if (this.formGroup.invalid) { return; @@ -203,7 +195,7 @@ export class LoginComponent implements OnInit, OnDestroy { const credentials = new PasswordLoginCredentials( email, masterPassword, - this.captchaToken, + null, // captcha no longer used in new login / registration scenarios null, ); @@ -212,13 +204,6 @@ export class LoginComponent implements OnInit, OnDestroy { await this.saveEmailSettings(); await this.handleAuthResult(authResult); - - if (this.clientType === ClientType.Desktop) { - if (this.captchaSiteKey) { - const content = document.getElementById("content") as HTMLDivElement; - content.setAttribute("style", "width:335px"); - } - } } catch (error) { this.logService.error(error); this.handleSubmitError(error); @@ -264,12 +249,6 @@ export class LoginComponent implements OnInit, OnDestroy { * to each if-condition block where necessary to stop code execution. */ private async handleAuthResult(authResult: AuthResult): Promise { - if (this.handleCaptchaRequired(authResult)) { - this.captchaSiteKey = authResult.captchaSiteKey; - this.captcha.init(authResult.captchaSiteKey); - return; - } - if (authResult.requiresEncryptionKeyMigration) { /* Legacy accounts used the master key to encrypt data. Migration is required but only performed on Web. */ @@ -359,10 +338,6 @@ export class LoginComponent implements OnInit, OnDestroy { ); } - protected showCaptcha(): boolean { - return !Utils.isNullOrWhitespace(this.captchaSiteKey); - } - protected async startAuthRequestLogin(): Promise { this.formGroup.get("masterPassword")?.clearValidators(); this.formGroup.get("masterPassword")?.updateValueAndValidity(); @@ -482,38 +457,6 @@ export class LoginComponent implements OnInit, OnDestroy { } } - private async setupCaptcha(): Promise { - const env = await firstValueFrom(this.environmentService.environment$); - const webVaultUrl = env.getWebVaultUrl(); - - this.captcha = new CaptchaIFrame( - window, - webVaultUrl, - this.i18nService, - (token: string) => { - this.captchaToken = token; - }, - (error: string) => { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: error, - }); - }, - (info: string) => { - this.toastService.showToast({ - variant: "info", - title: this.i18nService.t("info"), - message: info, - }); - }, - ); - } - - private handleCaptchaRequired(authResult: AuthResult): boolean { - return !Utils.isNullOrWhitespace(authResult.captchaSiteKey); - } - private async loadEmailSettings(): Promise { // Try to load the email from memory first const email = await firstValueFrom(this.loginEmailService.loginEmail$); diff --git a/libs/auth/src/common/abstractions/index.ts b/libs/auth/src/common/abstractions/index.ts index 093d703b74..88a13b490d 100644 --- a/libs/auth/src/common/abstractions/index.ts +++ b/libs/auth/src/common/abstractions/index.ts @@ -4,3 +4,4 @@ export * from "./login-email.service"; export * from "./login-strategy.service"; export * from "./user-decryption-options.service.abstraction"; export * from "./auth-request.service.abstraction"; +export * from "./login-approval-component.service.abstraction"; diff --git a/libs/auth/src/common/abstractions/login-approval-component.service.abstraction.ts b/libs/auth/src/common/abstractions/login-approval-component.service.abstraction.ts new file mode 100644 index 0000000000..eaa6235980 --- /dev/null +++ b/libs/auth/src/common/abstractions/login-approval-component.service.abstraction.ts @@ -0,0 +1,9 @@ +/** + * Abstraction for the LoginApprovalComponent service. + */ +export abstract class LoginApprovalComponentServiceAbstraction { + /** + * Shows a login requested alert if the window is not visible. + */ + abstract showLoginRequestedAlertIfWindowNotVisible: (email?: string) => Promise; +} diff --git a/libs/auth/src/common/abstractions/login-strategy.service.ts b/libs/auth/src/common/abstractions/login-strategy.service.ts index 7aeaf7ce7c..5d8e9bc58a 100644 --- a/libs/auth/src/common/abstractions/login-strategy.service.ts +++ b/libs/auth/src/common/abstractions/login-strategy.service.ts @@ -72,4 +72,8 @@ export abstract class LoginStrategyServiceAbstraction { * Creates a master key from the provided master password and email. */ makePreloginKey: (masterPassword: string, email: string) => Promise; + /** + * Emits true if the two factor session has expired. + */ + twoFactorTimeout$: Observable; } diff --git a/libs/auth/src/common/models/domain/login-credentials.ts b/libs/auth/src/common/models/domain/login-credentials.ts index bfe01aea20..28cf4e2d47 100644 --- a/libs/auth/src/common/models/domain/login-credentials.ts +++ b/libs/auth/src/common/models/domain/login-credentials.ts @@ -12,6 +12,7 @@ export class PasswordLoginCredentials { constructor( public email: string, public masterPassword: string, + // TODO: PM-15162 - captcha is deprecated as part of UI refresh work public captchaToken?: string, public twoFactor?: TokenTwoFactorRequest, ) {} diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index b86d1e3f3b..99e3c057e1 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -6,6 +6,7 @@ import { Observable, shareReplay, Subscription, + BehaviorSubject, } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -68,7 +69,7 @@ import { CACHE_KEY, } from "./login-strategy.state"; -const sessionTimeoutLength = 2 * 60 * 1000; // 2 minutes +const sessionTimeoutLength = 5 * 60 * 1000; // 5 minutes export class LoginStrategyService implements LoginStrategyServiceAbstraction { private sessionTimeoutSubscription: Subscription; @@ -76,6 +77,9 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { private loginStrategyCacheState: GlobalState; private loginStrategyCacheExpirationState: GlobalState; private authRequestPushNotificationState: GlobalState; + private twoFactorTimeoutSubject = new BehaviorSubject(false); + + twoFactorTimeout$: Observable = this.twoFactorTimeoutSubject.asObservable(); private loginStrategy$: Observable< | UserApiLoginStrategy @@ -123,7 +127,14 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { ); this.taskSchedulerService.registerTaskHandler( ScheduledTaskNames.loginStrategySessionTimeout, - () => this.clearCache(), + async () => { + this.twoFactorTimeoutSubject.next(true); + try { + await this.clearCache(); + } catch (e) { + this.logService.error("Failed to clear cache during session timeout", e); + } + }, ); this.currentAuthType$ = this.currentAuthnTypeState.state$; @@ -189,6 +200,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { | WebAuthnLoginCredentials, ): Promise { await this.clearCache(); + this.twoFactorTimeoutSubject.next(false); await this.currentAuthnTypeState.update((_) => credentials.type); @@ -273,6 +285,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { private async clearCache(): Promise { await this.currentAuthnTypeState.update((_) => null); await this.loginStrategyCacheState.update((_) => null); + this.twoFactorTimeoutSubject.next(false); await this.clearSessionTimeout(); } diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index e0be19986c..497d3b0889 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -164,13 +164,17 @@ export class Organization { return (this.isAdmin || this.permissions.accessEventLogs) && this.useEvents; } + /** + * Returns true if the user can access the Import page in the Admin Console. + * Note: this does not affect user access to the Import page in Password Manager, which can also be used to import + * into organization collections. + */ get canAccessImport() { return ( this.isProviderUser || this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin || - this.permissions.accessImportExport || - this.canCreateNewCollections // To allow users to create collections and then import into them + this.permissions.accessImportExport ); } diff --git a/libs/common/src/admin-console/services/policy/policy.service.ts b/libs/common/src/admin-console/services/policy/policy.service.ts index f52d061ad9..f1e3ac8adb 100644 --- a/libs/common/src/admin-console/services/policy/policy.service.ts +++ b/libs/common/src/admin-console/services/policy/policy.service.ts @@ -238,6 +238,9 @@ export class PolicyService implements InternalPolicyServiceAbstraction { case PolicyType.PersonalOwnership: // individual vault policy applies to everyone except admins and owners return organization.isAdmin; + case PolicyType.FreeFamiliesSponsorshipPolicy: + // free Bitwarden families policy applies to everyone + return false; default: return organization.canManagePolicies; } diff --git a/libs/common/src/auth/captcha-iframe.ts b/libs/common/src/auth/captcha-iframe.ts index 9a49532710..94859003e1 100644 --- a/libs/common/src/auth/captcha-iframe.ts +++ b/libs/common/src/auth/captcha-iframe.ts @@ -2,6 +2,7 @@ import { I18nService } from "../platform/abstractions/i18n.service"; import { IFrameComponent } from "./iframe-component"; +// TODO: PM-15162 - captcha is deprecated as part of UI refresh work export class CaptchaIFrame extends IFrameComponent { constructor( win: Window, diff --git a/libs/common/src/billing/abstractions/organization-billing.service.ts b/libs/common/src/billing/abstractions/organization-billing.service.ts index 72902baa30..0bc1f3bc55 100644 --- a/libs/common/src/billing/abstractions/organization-billing.service.ts +++ b/libs/common/src/billing/abstractions/organization-billing.service.ts @@ -1,3 +1,6 @@ +import { BillingSourceResponse } from "@bitwarden/common/billing/models/response/billing.response"; +import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response"; + import { OrganizationResponse } from "../../admin-console/models/response/organization.response"; import { InitiationPath } from "../../models/request/reference-event.request"; import { PaymentMethodType, PlanType } from "../enums"; @@ -41,11 +44,15 @@ export type SubscriptionInformation = { }; export abstract class OrganizationBillingServiceAbstraction { - purchaseSubscription: (subscription: SubscriptionInformation) => Promise; + getPaymentSource: ( + organizationId: string, + ) => Promise; - startFree: (subscription: SubscriptionInformation) => Promise; + purchaseSubscription: (subscription: SubscriptionInformation) => Promise; purchaseSubscriptionNoPaymentMethod: ( subscription: SubscriptionInformation, ) => Promise; + + startFree: (subscription: SubscriptionInformation) => Promise; } diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts index efc3627853..487098620b 100644 --- a/libs/common/src/billing/services/organization-billing.service.ts +++ b/libs/common/src/billing/services/organization-billing.service.ts @@ -1,4 +1,18 @@ -import { KeyService } from "../../../../key-management/src/abstractions/key.service"; +import { + BillingApiServiceAbstraction, + OrganizationBillingServiceAbstraction, + OrganizationInformation, + PaymentInformation, + PlanInformation, + SubscriptionInformation, +} from "@bitwarden/common/billing/abstractions"; +import { BillingSourceResponse } from "@bitwarden/common/billing/models/response/billing.response"; +import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { KeyService } from "@bitwarden/key-management"; + import { ApiService } from "../../abstractions/api.service"; import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request"; @@ -8,14 +22,6 @@ import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { OrgKey } from "../../types/key"; -import { SyncService } from "../../vault/abstractions/sync/sync.service.abstraction"; -import { - OrganizationBillingServiceAbstraction, - OrganizationInformation, - PaymentInformation, - PlanInformation, - SubscriptionInformation, -} from "../abstractions/organization-billing.service"; import { PlanType } from "../enums"; import { OrganizationNoPaymentMethodCreateRequest } from "../models/request/organization-no-payment-method-create-request"; @@ -29,6 +35,8 @@ interface OrganizationKeys { export class OrganizationBillingService implements OrganizationBillingServiceAbstraction { constructor( private apiService: ApiService, + private billingApiService: BillingApiServiceAbstraction, + private configService: ConfigService, private keyService: KeyService, private encryptService: EncryptService, private i18nService: I18nService, @@ -36,6 +44,23 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs private syncService: SyncService, ) {} + async getPaymentSource( + organizationId: string, + ): Promise { + const deprecateStripeSourcesAPI = await this.configService.getFeatureFlag( + FeatureFlag.AC2476_DeprecateStripeSourcesAPI, + ); + + if (deprecateStripeSourcesAPI) { + const paymentMethod = + await this.billingApiService.getOrganizationPaymentMethod(organizationId); + return paymentMethod.paymentSource; + } else { + const billing = await this.organizationApiService.getBilling(organizationId); + return billing.paymentSource; + } + } + async purchaseSubscription(subscription: SubscriptionInformation): Promise { const request = new OrganizationCreateRequest(); @@ -58,26 +83,6 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs return response; } - async startFree(subscription: SubscriptionInformation): Promise { - const request = new OrganizationCreateRequest(); - - const organizationKeys = await this.makeOrganizationKeys(); - - this.setOrganizationKeys(request, organizationKeys); - - this.setOrganizationInformation(request, subscription.organization); - - this.setPlanInformation(request, subscription.plan); - - const response = await this.organizationApiService.create(request); - - await this.apiService.refreshIdentityToken(); - - await this.syncService.fullSync(true); - - return response; - } - async purchaseSubscriptionNoPaymentMethod( subscription: SubscriptionInformation, ): Promise { @@ -100,6 +105,26 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs return response; } + async startFree(subscription: SubscriptionInformation): Promise { + const request = new OrganizationCreateRequest(); + + const organizationKeys = await this.makeOrganizationKeys(); + + this.setOrganizationKeys(request, organizationKeys); + + this.setOrganizationInformation(request, subscription.organization); + + this.setPlanInformation(request, subscription.plan); + + const response = await this.organizationApiService.create(request); + + await this.apiService.refreshIdentityToken(); + + await this.syncService.fullSync(true); + + return response; + } + private async makeOrganizationKeys(): Promise { const [encryptedKey, key] = await this.keyService.makeOrgKey(); const [publicKey, encryptedPrivateKey] = await this.keyService.makeKeyPair(key); diff --git a/libs/common/src/platform/services/file-upload/bitwarden-file-upload.service.ts b/libs/common/src/platform/services/file-upload/bitwarden-file-upload.service.ts index 3d0c31ef3e..9359440530 100644 --- a/libs/common/src/platform/services/file-upload/bitwarden-file-upload.service.ts +++ b/libs/common/src/platform/services/file-upload/bitwarden-file-upload.service.ts @@ -8,22 +8,21 @@ export class BitwardenFileUploadService { apiCall: (fd: FormData) => Promise, ) { const fd = new FormData(); - try { + + if (Utils.isBrowser) { const blob = new Blob([encryptedFileData.buffer], { type: "application/octet-stream" }); fd.append("data", blob, encryptedFileName); - } catch (e) { - if (Utils.isNode && !Utils.isBrowser) { - fd.append( - "data", - Buffer.from(encryptedFileData.buffer) as any, - { - filepath: encryptedFileName, - contentType: "application/octet-stream", - } as any, - ); - } else { - throw e; - } + } else if (Utils.isNode) { + fd.append( + "data", + Buffer.from(encryptedFileData.buffer) as any, + { + filename: encryptedFileName, + contentType: "application/octet-stream", + } as any, + ); + } else { + throw new Error("Unsupported environment"); } await apiCall(fd); diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index 4506319eed..424c32761a 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -32,13 +32,33 @@ import { SdkService } from "../../abstractions/sdk/sdk.service"; import { compareValues } from "../../misc/compare-values"; import { EncryptedString } from "../../models/domain/enc-string"; +export class RecoverableSDKError extends Error { + sdk: BitwardenClient; + timeout: number; + + constructor(sdk: BitwardenClient, timeout: number) { + super(`SDK took ${timeout}s to initialize`); + + this.sdk = sdk; + this.timeout = timeout; + } +} + export class DefaultSdkService implements SdkService { private sdkClientCache = new Map>(); client$ = this.environmentService.environment$.pipe( concatMap(async (env) => { const settings = this.toSettings(env); - return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info); + try { + return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info); + } catch (e) { + if (e instanceof RecoverableSDKError) { + await this.failedToInitialize("sdk", e); + return e.sdk; + } + throw e; + } }), shareReplay({ refCount: true, bufferSize: 1 }), ); diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 0779d80982..a600901df4 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -121,6 +121,10 @@ export const TRANSLATION_DISK = new StateDefinition("translation", "disk", { web export const ANIMATION_DISK = new StateDefinition("animation", "disk"); export const TASK_SCHEDULER_DISK = new StateDefinition("taskScheduler", "disk"); +// Design System + +export const POPUP_STYLE_DISK = new StateDefinition("popupStyle", "disk"); + // Secrets Manager export const SM_ONBOARDING_DISK = new StateDefinition("smOnboarding", "disk", { diff --git a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts new file mode 100644 index 0000000000..12257905d1 --- /dev/null +++ b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.spec.ts @@ -0,0 +1,492 @@ +import { mock } from "jest-mock-extended"; +import { BehaviorSubject, Subject } from "rxjs"; + +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrgKey, UserKey } from "@bitwarden/common/types/key"; +import { KeyService } from "@bitwarden/key-management"; + +import { OrganizationBound, UserBound } from "../dependencies"; + +import { KeyServiceLegacyEncryptorProvider } from "./key-service-legacy-encryptor-provider"; +import { OrganizationEncryptor } from "./organization-encryptor.abstraction"; +import { OrganizationKeyEncryptor } from "./organization-key-encryptor"; +import { UserEncryptor } from "./user-encryptor.abstraction"; +import { UserKeyEncryptor } from "./user-key-encryptor"; + +const encryptService = mock(); +const keyService = mock(); + +const SomeCsprngArray = new Uint8Array(64) as CsprngArray; +const SomeUser = "some user" as UserId; +const AnotherUser = "another user" as UserId; +const SomeUserKey = new SymmetricCryptoKey(SomeCsprngArray) as UserKey; +const SomeOrganization = "some organization" as OrganizationId; +const AnotherOrganization = "another organization" as OrganizationId; +const SomeOrgKey = new SymmetricCryptoKey(SomeCsprngArray) as OrgKey; +const AnotherOrgKey = new SymmetricCryptoKey(SomeCsprngArray) as OrgKey; +const OrgRecords: Record = { + [SomeOrganization]: SomeOrgKey, + [AnotherOrganization]: AnotherOrgKey, +}; + +// Many tests examine the private members of the objects constructed by the +// provider. This is necessary because it's not presently possible to spy +// on the constructors directly. +describe("KeyServiceLegacyEncryptorProvider", () => { + describe("userEncryptor$", () => { + it("emits a user key encryptor bound to the user", async () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: UserBound<"encryptor", UserEncryptor>[] = []; + + provider.userEncryptor$(1, { singleUserId$ }).subscribe((v) => results.push(v)); + + expect(keyService.userKey$).toHaveBeenCalledWith(SomeUser); + expect(results.length).toBe(1); + expect(results[0]).toMatchObject({ + userId: SomeUser, + encryptor: { + userId: SomeUser, + key: SomeUserKey, + dataPacker: { frameSize: 1 }, + }, + }); + expect(results[0].encryptor).toBeInstanceOf(UserKeyEncryptor); + }); + + it("waits until `dependencies.singleUserId$` emits", () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new Subject(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: UserBound<"encryptor", UserEncryptor>[] = []; + provider.userEncryptor$(1, { singleUserId$ }).subscribe((v) => results.push(v)); + // precondition: no emissions occur on subscribe + expect(results.length).toBe(0); + + singleUserId$.next(SomeUser); + + expect(results.length).toBe(1); + }); + + it("emits a new user key encryptor each time `dependencies.singleUserId$` emits", () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new Subject(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: UserBound<"encryptor", UserEncryptor>[] = []; + provider.userEncryptor$(1, { singleUserId$ }).subscribe((v) => results.push(v)); + + singleUserId$.next(SomeUser); + singleUserId$.next(SomeUser); + + expect(results.length).toBe(2); + expect(results[0]).not.toBe(results[1]); + }); + + it("waits until `userKey$` emits a truthy value", () => { + const userKey$ = new BehaviorSubject(null); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: UserBound<"encryptor", UserEncryptor>[] = []; + provider.userEncryptor$(1, { singleUserId$ }).subscribe((v) => results.push(v)); + // precondition: no emissions occur on subscribe + expect(results.length).toBe(0); + + userKey$.next(SomeUserKey); + + expect(results.length).toBe(1); + expect(results[0]).toMatchObject({ + userId: SomeUser, + encryptor: { + userId: SomeUser, + key: SomeUserKey, + dataPacker: { frameSize: 1 }, + }, + }); + }); + + it("emits a user key encryptor each time `userKey$` emits", () => { + const userKey$ = new Subject(); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: UserBound<"encryptor", UserEncryptor>[] = []; + provider.userEncryptor$(1, { singleUserId$ }).subscribe((v) => results.push(v)); + + userKey$.next(SomeUserKey); + userKey$.next(SomeUserKey); + + expect(results.length).toBe(2); + }); + + it("errors when the userId changes", () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new Subject(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .userEncryptor$(1, { singleUserId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + singleUserId$.next(SomeUser); + singleUserId$.next(AnotherUser); + + expect(error).toEqual({ expectedUserId: SomeUser, actualUserId: AnotherUser }); + }); + + it("errors when `dependencies.singleUserId$` errors", () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new Subject(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .userEncryptor$(1, { singleUserId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + singleUserId$.error({ some: "error" }); + + expect(error).toEqual({ some: "error" }); + }); + + it("errors once `dependencies.singleUserId$` emits and `userKey$` errors", () => { + const userKey$ = new Subject(); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .userEncryptor$(1, { singleUserId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + userKey$.error({ some: "error" }); + + expect(error).toEqual({ some: "error" }); + }); + + it("completes when `dependencies.singleUserId$` completes", () => { + const userKey$ = new Subject(); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let completed = false; + provider + .userEncryptor$(1, { singleUserId$ }) + .subscribe({ complete: () => (completed = true) }); + + singleUserId$.complete(); + + expect(completed).toBeTrue(); + }); + + it("completes when `userKey$` emits a falsy value after emitting a truthy value", () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let completed = false; + provider + .userEncryptor$(1, { singleUserId$ }) + .subscribe({ complete: () => (completed = true) }); + + userKey$.next(null); + + expect(completed).toBeTrue(); + }); + + it("completes once `dependencies.singleUserId$` emits and `userKey$` completes", () => { + const userKey$ = new BehaviorSubject(SomeUserKey); + keyService.userKey$.mockReturnValue(userKey$); + const singleUserId$ = new BehaviorSubject(SomeUser); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let completed = false; + provider + .userEncryptor$(1, { singleUserId$ }) + .subscribe({ complete: () => (completed = true) }); + + userKey$.complete(); + + expect(completed).toBeTrue(); + }); + }); + + describe("organizationEncryptor$", () => { + it("emits an organization key encryptor bound to the organization", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: OrganizationBound<"encryptor", OrganizationEncryptor>[] = []; + + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe((v) => results.push(v)); + + expect(keyService.orgKeys$).toHaveBeenCalledWith(SomeUser); + expect(results.length).toBe(1); + expect(results[0]).toMatchObject({ + organizationId: SomeOrganization, + encryptor: { + organizationId: SomeOrganization, + key: SomeOrgKey, + dataPacker: { frameSize: 1 }, + }, + }); + expect(results[0].encryptor).toBeInstanceOf(OrganizationKeyEncryptor); + }); + + it("waits until `dependencies.singleOrganizationId$` emits", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new Subject>(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: OrganizationBound<"encryptor", OrganizationEncryptor>[] = []; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe((v) => results.push(v)); + // precondition: no emissions occur on subscribe + expect(results.length).toBe(0); + + singleOrganizationId$.next({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + + expect(results.length).toBe(1); + }); + + it("emits a new organization key encryptor when `dependencies.singleOrganizationId$` emits", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new Subject>(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: OrganizationBound<"encryptor", OrganizationEncryptor>[] = []; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe((v) => results.push(v)); + // precondition: no emissions occur on subscribe + expect(results.length).toBe(0); + + singleOrganizationId$.next({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + singleOrganizationId$.next({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + + expect(results.length).toBe(2); + expect(results[0]).not.toBe(results[1]); + }); + + it("waits until `orgKeys$` emits a truthy value", () => { + const orgKey$ = new BehaviorSubject>(null); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: OrganizationBound<"encryptor", OrganizationEncryptor>[] = []; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe((v) => results.push(v)); + // precondition: no emissions occur on subscribe + expect(results.length).toBe(0); + + orgKey$.next(OrgRecords); + + expect(results.length).toBe(1); + expect(results[0]).toMatchObject({ + organizationId: SomeOrganization, + encryptor: { + organizationId: SomeOrganization, + key: SomeOrgKey, + dataPacker: { frameSize: 1 }, + }, + }); + }); + + it("emits an organization key encryptor each time `orgKeys$` emits", () => { + const orgKey$ = new Subject>(); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + const results: OrganizationBound<"encryptor", OrganizationEncryptor>[] = []; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe((v) => results.push(v)); + + orgKey$.next(OrgRecords); + orgKey$.next(OrgRecords); + + expect(results.length).toBe(2); + }); + + it("errors when the userId changes", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new Subject>(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + singleOrganizationId$.next({ userId: SomeUser, organizationId: SomeOrganization }); + singleOrganizationId$.next({ userId: AnotherUser, organizationId: SomeOrganization }); + + expect(error).toEqual({ expectedUserId: SomeUser, actualUserId: AnotherUser }); + }); + + it("errors when the organizationId changes", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new Subject>(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + singleOrganizationId$.next({ userId: SomeUser, organizationId: SomeOrganization }); + singleOrganizationId$.next({ userId: SomeUser, organizationId: AnotherOrganization }); + + expect(error).toEqual({ + expectedOrganizationId: SomeOrganization, + actualOrganizationId: AnotherOrganization, + }); + }); + + it("errors when `dependencies.singleOrganizationId$` errors", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new Subject>(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + singleOrganizationId$.error({ some: "error" }); + + expect(error).toEqual({ some: "error" }); + }); + + it("errors once `dependencies.singleOrganizationId$` emits and `orgKeys$` errors", () => { + const orgKey$ = new Subject>(); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + orgKey$.error({ some: "error" }); + + expect(error).toEqual({ some: "error" }); + }); + + it("errors when the user lacks the requested org key", () => { + const orgKey$ = new BehaviorSubject>({}); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let error: unknown = false; + + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ error: (e: unknown) => (error = e) }); + + expect(error).toBeInstanceOf(Error); + }); + + it("completes when `dependencies.singleOrganizationId$` completes", () => { + const orgKey$ = new BehaviorSubject(OrgRecords); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new Subject>(); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let completed = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ complete: () => (completed = true) }); + + singleOrganizationId$.complete(); + + expect(completed).toBeTrue(); + }); + + it("completes when `orgKeys$` emits a falsy value after emitting a truthy value", () => { + const orgKey$ = new Subject>(); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let completed = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ complete: () => (completed = true) }); + + orgKey$.next(OrgRecords); + orgKey$.next(null); + + expect(completed).toBeTrue(); + }); + + it("completes once `dependencies.singleOrganizationId$` emits and `userKey$` completes", () => { + const orgKey$ = new Subject>(); + keyService.orgKeys$.mockReturnValue(orgKey$); + const singleOrganizationId$ = new BehaviorSubject< + UserBound<"organizationId", OrganizationId> + >({ + organizationId: SomeOrganization, + userId: SomeUser, + }); + const provider = new KeyServiceLegacyEncryptorProvider(encryptService, keyService); + let completed = false; + provider + .organizationEncryptor$(1, { singleOrganizationId$ }) + .subscribe({ complete: () => (completed = true) }); + + orgKey$.complete(); + + expect(completed).toBeTrue(); + }); + }); +}); diff --git a/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts new file mode 100644 index 0000000000..f3d6c82ffc --- /dev/null +++ b/libs/common/src/tools/cryptography/key-service-legacy-encryptor-provider.ts @@ -0,0 +1,132 @@ +import { + connect, + dematerialize, + map, + materialize, + ReplaySubject, + skipWhile, + switchMap, + takeUntil, + takeWhile, +} from "rxjs"; + +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; + +import { + OrganizationBound, + SingleOrganizationDependency, + SingleUserDependency, + UserBound, +} from "../dependencies"; +import { anyComplete, errorOnChange } from "../rx"; +import { PaddedDataPacker } from "../state/padded-data-packer"; + +import { LegacyEncryptorProvider } from "./legacy-encryptor-provider"; +import { OrganizationEncryptor } from "./organization-encryptor.abstraction"; +import { OrganizationKeyEncryptor } from "./organization-key-encryptor"; +import { UserEncryptor } from "./user-encryptor.abstraction"; +import { UserKeyEncryptor } from "./user-key-encryptor"; + +/** Creates encryptors + */ +export class KeyServiceLegacyEncryptorProvider implements LegacyEncryptorProvider { + /** Instantiates the legacy encryptor provider. + * @param encryptService injected into encryptors to perform encryption + * @param keyService looks up keys for construction into an encryptor + */ + constructor( + private readonly encryptService: EncryptService, + private readonly keyService: KeyService, + ) {} + + userEncryptor$(frameSize: number, dependencies: SingleUserDependency) { + const packer = new PaddedDataPacker(frameSize); + const encryptor$ = dependencies.singleUserId$.pipe( + errorOnChange( + (userId) => userId, + (expectedUserId, actualUserId) => ({ expectedUserId, actualUserId }), + ), + connect((singleUserId$) => { + const singleUserId = new ReplaySubject(1); + singleUserId$.subscribe(singleUserId); + + return singleUserId.pipe( + switchMap((userId) => + this.keyService.userKey$(userId).pipe( + // wait until the key becomes available + skipWhile((key) => !key), + // complete when the key becomes unavailable + takeWhile((key) => !!key), + map((key) => { + const encryptor = new UserKeyEncryptor(userId, this.encryptService, key, packer); + + return { userId, encryptor } satisfies UserBound<"encryptor", UserEncryptor>; + }), + materialize(), + ), + ), + dematerialize(), + takeUntil(anyComplete(singleUserId)), + ); + }), + ); + + return encryptor$; + } + + organizationEncryptor$(frameSize: number, dependencies: SingleOrganizationDependency) { + const packer = new PaddedDataPacker(frameSize); + const encryptor$ = dependencies.singleOrganizationId$.pipe( + errorOnChange( + (pair) => pair.userId, + (expectedUserId, actualUserId) => ({ expectedUserId, actualUserId }), + ), + errorOnChange( + (pair) => pair.organizationId, + (expectedOrganizationId, actualOrganizationId) => ({ + expectedOrganizationId, + actualOrganizationId, + }), + ), + connect((singleOrganizationId$) => { + const singleOrganizationId = new ReplaySubject>( + 1, + ); + singleOrganizationId$.subscribe(singleOrganizationId); + + return singleOrganizationId.pipe( + switchMap((pair) => + this.keyService.orgKeys$(pair.userId).pipe( + // wait until the key becomes available + skipWhile((keys) => !keys), + // complete when the key becomes unavailable + takeWhile((keys) => !!keys), + map((keys) => { + const organizationId = pair.organizationId; + const key = keys[organizationId]; + const encryptor = new OrganizationKeyEncryptor( + organizationId, + this.encryptService, + key, + packer, + ); + + return { organizationId, encryptor } satisfies OrganizationBound< + "encryptor", + OrganizationEncryptor + >; + }), + materialize(), + ), + ), + dematerialize(), + takeUntil(anyComplete(singleOrganizationId)), + ); + }), + ); + + return encryptor$; + } +} diff --git a/libs/common/src/tools/cryptography/legacy-encryptor-provider.ts b/libs/common/src/tools/cryptography/legacy-encryptor-provider.ts new file mode 100644 index 0000000000..5e83cb0671 --- /dev/null +++ b/libs/common/src/tools/cryptography/legacy-encryptor-provider.ts @@ -0,0 +1,42 @@ +import { Observable } from "rxjs"; + +import { + OrganizationBound, + SingleOrganizationDependency, + SingleUserDependency, + UserBound, +} from "../dependencies"; + +import { OrganizationEncryptor } from "./organization-encryptor.abstraction"; +import { UserEncryptor } from "./user-encryptor.abstraction"; + +/** Creates encryptors + * @deprecated this logic will soon be replaced with a design that provides for + * key rotation. Use it at your own risk + */ +export abstract class LegacyEncryptorProvider { + /** Retrieves an encryptor populated with the user's most recent key instance that + * uses a padded data packer to encode data. + * @param frameSize length of the padded data packer's frames. + * @param dependencies.singleUserId$ identifies the user to which the encryptor is bound + * @returns an observable that emits when the key becomes available and completes + * when the key becomes unavailable. + */ + userEncryptor$: ( + frameSize: number, + dependencies: SingleUserDependency, + ) => Observable>; + + /** Retrieves an encryptor populated with the organization's most recent key instance that + * uses a padded data packer to encode data. + * @param frameSize length of the padded data packer's frames. + * @param dependencies.singleOrganizationId$ identifies the user/org combination + * to which the encryptor is bound. + * @returns an observable that emits when the key becomes available and completes + * when the key becomes unavailable. + */ + organizationEncryptor$: ( + frameSize: number, + dependences: SingleOrganizationDependency, + ) => Observable>; +} diff --git a/libs/common/src/tools/cryptography/organization-encryptor.abstraction.ts b/libs/common/src/tools/cryptography/organization-encryptor.abstraction.ts new file mode 100644 index 0000000000..6884cdf38a --- /dev/null +++ b/libs/common/src/tools/cryptography/organization-encryptor.abstraction.ts @@ -0,0 +1,33 @@ +import { Jsonify } from "type-fest"; + +import { OrganizationId } from "@bitwarden/common/types/guid"; + +import { EncString } from "../../platform/models/domain/enc-string"; + +/** An encryption strategy that protects a type's secrets with + * organization-specific keys. This strategy is bound to a specific organization. + */ +export abstract class OrganizationEncryptor { + /** Identifies the organization bound to the encryptor. */ + readonly organizationId: OrganizationId; + + /** Protects secrets in `value` with an organization-specific key. + * @param secret the object to protect. This object is mutated during encryption. + * @returns a promise that resolves to a tuple. The tuple's first property contains + * the encrypted secret and whose second property contains an object w/ disclosed + * properties. + * @throws If `value` is `null` or `undefined`, the promise rejects with an error. + */ + abstract encrypt(secret: Jsonify): Promise; + + /** Combines protected secrets and disclosed data into a type that can be + * rehydrated into a domain object. + * @param secret an encrypted JSON payload containing encrypted secrets. + * @returns a promise that resolves to the raw state. This state *is not* a + * class. It contains only data that can be round-tripped through JSON, + * and lacks members such as a prototype or bound functions. + * @throws If `secret` or `disclosed` is `null` or `undefined`, the promise + * rejects with an error. + */ + abstract decrypt(secret: EncString): Promise>; +} diff --git a/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts b/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts new file mode 100644 index 0000000000..62c8ea24ae --- /dev/null +++ b/libs/common/src/tools/cryptography/organization-key-encryptor.spec.ts @@ -0,0 +1,125 @@ +import { mock } from "jest-mock-extended"; + +import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncString } from "../../platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "../../types/csprng"; +import { OrganizationId } from "../../types/guid"; +import { OrgKey } from "../../types/key"; +import { DataPacker } from "../state/data-packer.abstraction"; + +import { OrganizationKeyEncryptor } from "./organization-key-encryptor"; + +describe("OrgKeyEncryptor", () => { + const encryptService = mock(); + const dataPacker = mock(); + const orgKey = new SymmetricCryptoKey(new Uint8Array(64) as CsprngArray) as OrgKey; + const anyOrgId = "foo" as OrganizationId; + + beforeEach(() => { + // The OrgKeyEncryptor is, in large part, a facade coordinating a handful of worker + // objects, so its tests focus on how data flows between components. The defaults rely + // on this property--that the facade treats its data like a opaque objects--to trace + // the data through several function calls. Should the encryptor interact with the + // objects themselves, these mocks will break. + encryptService.encrypt.mockImplementation((p) => Promise.resolve(p as unknown as EncString)); + encryptService.decryptToUtf8.mockImplementation((c) => Promise.resolve(c as unknown as string)); + dataPacker.pack.mockImplementation((v) => v as string); + dataPacker.unpack.mockImplementation((v: string) => v as T); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe("constructor", () => { + it("should set organizationId", async () => { + const encryptor = new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, dataPacker); + expect(encryptor.organizationId).toEqual(anyOrgId); + }); + + it("should throw if organizationId was not supplied", async () => { + expect(() => new OrganizationKeyEncryptor(null, encryptService, orgKey, dataPacker)).toThrow( + "organizationId cannot be null or undefined", + ); + expect(() => new OrganizationKeyEncryptor(null, encryptService, orgKey, dataPacker)).toThrow( + "organizationId cannot be null or undefined", + ); + }); + + it("should throw if encryptService was not supplied", async () => { + expect(() => new OrganizationKeyEncryptor(anyOrgId, null, orgKey, dataPacker)).toThrow( + "encryptService cannot be null or undefined", + ); + expect(() => new OrganizationKeyEncryptor(anyOrgId, null, orgKey, dataPacker)).toThrow( + "encryptService cannot be null or undefined", + ); + }); + + it("should throw if key was not supplied", async () => { + expect( + () => new OrganizationKeyEncryptor(anyOrgId, encryptService, null, dataPacker), + ).toThrow("key cannot be null or undefined"); + expect( + () => new OrganizationKeyEncryptor(anyOrgId, encryptService, null, dataPacker), + ).toThrow("key cannot be null or undefined"); + }); + + it("should throw if dataPacker was not supplied", async () => { + expect(() => new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, null)).toThrow( + "dataPacker cannot be null or undefined", + ); + expect(() => new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, null)).toThrow( + "dataPacker cannot be null or undefined", + ); + }); + }); + + describe("encrypt", () => { + it("should throw if value was not supplied", async () => { + const encryptor = new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, dataPacker); + + await expect(encryptor.encrypt>(null)).rejects.toThrow( + "secret cannot be null or undefined", + ); + await expect(encryptor.encrypt>(undefined)).rejects.toThrow( + "secret cannot be null or undefined", + ); + }); + + it("should encrypt a packed value using the organization's key", async () => { + const encryptor = new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, dataPacker); + const value = { foo: true }; + + const result = await encryptor.encrypt(value); + + // these are data flow expectations; the operations all all pass-through mocks + expect(dataPacker.pack).toHaveBeenCalledWith(value); + expect(encryptService.encrypt).toHaveBeenCalledWith(value, orgKey); + expect(result).toBe(value); + }); + }); + + describe("decrypt", () => { + it("should throw if secret was not supplied", async () => { + const encryptor = new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, dataPacker); + + await expect(encryptor.decrypt(null)).rejects.toThrow("secret cannot be null or undefined"); + await expect(encryptor.decrypt(undefined)).rejects.toThrow( + "secret cannot be null or undefined", + ); + }); + + it("should declassify a decrypted packed value using the organization's key", async () => { + const encryptor = new OrganizationKeyEncryptor(anyOrgId, encryptService, orgKey, dataPacker); + const secret = "encrypted" as any; + + const result = await encryptor.decrypt(secret); + + // these are data flow expectations; the operations all all pass-through mocks + expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(secret, orgKey); + expect(dataPacker.unpack).toHaveBeenCalledWith(secret); + expect(result).toBe(secret); + }); + }); +}); diff --git a/libs/common/src/tools/cryptography/organization-key-encryptor.ts b/libs/common/src/tools/cryptography/organization-key-encryptor.ts new file mode 100644 index 0000000000..5bd7e36ee2 --- /dev/null +++ b/libs/common/src/tools/cryptography/organization-key-encryptor.ts @@ -0,0 +1,60 @@ +import { Jsonify } from "type-fest"; + +import { OrganizationId } from "@bitwarden/common/types/guid"; + +import { EncryptService } from "../../platform/abstractions/encrypt.service"; +import { EncString } from "../../platform/models/domain/enc-string"; +import { OrgKey } from "../../types/key"; +import { DataPacker } from "../state/data-packer.abstraction"; + +import { OrganizationEncryptor } from "./organization-encryptor.abstraction"; + +/** A classification strategy that protects a type's secrets by encrypting them + * with an `OrgKey` + */ +export class OrganizationKeyEncryptor extends OrganizationEncryptor { + /** Instantiates the encryptor + * @param organizationId identifies the organization bound to the encryptor. + * @param encryptService protects properties of `Secret`. + * @param key the key instance protecting the data. + * @param dataPacker packs and unpacks data classified as secrets. + */ + constructor( + readonly organizationId: OrganizationId, + private readonly encryptService: EncryptService, + private readonly key: OrgKey, + private readonly dataPacker: DataPacker, + ) { + super(); + this.assertHasValue("organizationId", organizationId); + this.assertHasValue("key", key); + this.assertHasValue("dataPacker", dataPacker); + this.assertHasValue("encryptService", encryptService); + } + + async encrypt(secret: Jsonify): Promise { + this.assertHasValue("secret", secret); + + let packed = this.dataPacker.pack(secret); + const encrypted = await this.encryptService.encrypt(packed, this.key); + packed = null; + + return encrypted; + } + + async decrypt(secret: EncString): Promise> { + this.assertHasValue("secret", secret); + + let decrypted = await this.encryptService.decryptToUtf8(secret, this.key); + const unpacked = this.dataPacker.unpack(decrypted); + decrypted = null; + + return unpacked; + } + + private assertHasValue(name: string, value: any) { + if (value === undefined || value === null) { + throw new Error(`${name} cannot be null or undefined`); + } + } +} diff --git a/libs/common/src/tools/state/user-encryptor.abstraction.ts b/libs/common/src/tools/cryptography/user-encryptor.abstraction.ts similarity index 100% rename from libs/common/src/tools/state/user-encryptor.abstraction.ts rename to libs/common/src/tools/cryptography/user-encryptor.abstraction.ts diff --git a/libs/common/src/tools/state/user-key-encryptor.spec.ts b/libs/common/src/tools/cryptography/user-key-encryptor.spec.ts similarity index 98% rename from libs/common/src/tools/state/user-key-encryptor.spec.ts rename to libs/common/src/tools/cryptography/user-key-encryptor.spec.ts index 37c1155488..5b0ee5103c 100644 --- a/libs/common/src/tools/state/user-key-encryptor.spec.ts +++ b/libs/common/src/tools/cryptography/user-key-encryptor.spec.ts @@ -6,8 +6,8 @@ import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypt import { CsprngArray } from "../../types/csprng"; import { UserId } from "../../types/guid"; import { UserKey } from "../../types/key"; +import { DataPacker } from "../state/data-packer.abstraction"; -import { DataPacker } from "./data-packer.abstraction"; import { UserKeyEncryptor } from "./user-key-encryptor"; describe("UserKeyEncryptor", () => { diff --git a/libs/common/src/tools/state/user-key-encryptor.ts b/libs/common/src/tools/cryptography/user-key-encryptor.ts similarity index 96% rename from libs/common/src/tools/state/user-key-encryptor.ts rename to libs/common/src/tools/cryptography/user-key-encryptor.ts index d0316636d2..b2ccc51301 100644 --- a/libs/common/src/tools/state/user-key-encryptor.ts +++ b/libs/common/src/tools/cryptography/user-key-encryptor.ts @@ -5,8 +5,8 @@ import { UserId } from "@bitwarden/common/types/guid"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { EncString } from "../../platform/models/domain/enc-string"; import { UserKey } from "../../types/key"; +import { DataPacker } from "../state/data-packer.abstraction"; -import { DataPacker } from "./data-packer.abstraction"; import { UserEncryptor } from "./user-encryptor.abstraction"; /** A classification strategy that protects a type's secrets by encrypting them diff --git a/libs/common/src/tools/dependencies.ts b/libs/common/src/tools/dependencies.ts index 84e2f53fa2..cdae45bc94 100644 --- a/libs/common/src/tools/dependencies.ts +++ b/libs/common/src/tools/dependencies.ts @@ -1,9 +1,10 @@ import { Observable } from "rxjs"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; -import { UserEncryptor } from "./state/user-encryptor.abstraction"; +import { OrganizationEncryptor } from "./cryptography/organization-encryptor.abstraction"; +import { UserEncryptor } from "./cryptography/user-encryptor.abstraction"; /** error emitted when the `SingleUserDependency` changes Ids */ export type UserChangedError = { @@ -13,6 +14,14 @@ export type UserChangedError = { actualUserId: UserId; }; +/** error emitted when the `SingleOrganizationDependency` changes Ids */ +export type OrganizationChangedError = { + /** the organizationId pinned by the single organization dependency */ + expectedOrganizationId: OrganizationId; + /** the organizationId received in error */ + actualOrganizationId: OrganizationId; +}; + /** A pattern for types that depend upon a dynamic policy stream and return * an observable. * @@ -55,6 +64,54 @@ export type UserBound = { [P in K]: T } & { userId: UserId; }; +/** Decorates a type to indicate the organization, if any, that the type is usable only by + * a specific organization. + */ +export type OrganizationBound = { [P in K]: T } & { + /** The organization to which T is bound. */ + organizationId: OrganizationId; +}; + +/** A pattern for types that depend upon a fixed-key encryptor and return + * an observable. + * + * Consumers of this dependency should emit a `OrganizationChangedError` if + * the bound OrganizationId changes or if the encryptor changes. If + * `singleOrganizationEncryptor$` completes, the consumer should complete + * once all events received prior to the completion event are + * finished processing. The consumer should, where possible, + * prioritize these events in order to complete as soon as possible. + * If `singleOrganizationEncryptor$` emits an unrecoverable error, the consumer + * should also emit the error. + */ +export type SingleOrganizationEncryptorDependency = { + /** A stream that emits an encryptor when subscribed and the org key + * is available, and completes when the org key is no longer available. + * The stream should not emit null or undefined. + */ + singleOrgEncryptor$: Observable>; +}; + +/** A pattern for types that depend upon a fixed-value organizationId and return + * an observable. + * + * Consumers of this dependency should emit a `OrganizationChangedError` if + * the value of `singleOrganizationId$` changes. If `singleOrganizationId$` completes, + * the consumer should also complete. If `singleOrganizationId$` errors, the + * consumer should also emit the error. + * + * @remarks Check the consumer's documentation to determine how it + * responds to repeat emissions. + */ +export type SingleOrganizationDependency = { + /** A stream that emits an organization Id and the user to which it is bound + * when subscribed and the user's account is unlocked, and completes when the + * account is locked or logged out. + * The stream should not emit null or undefined. + */ + singleOrganizationId$: Observable>; +}; + /** A pattern for types that depend upon a fixed-key encryptor and return * an observable. * diff --git a/libs/common/src/tools/rx.spec.ts b/libs/common/src/tools/rx.spec.ts index f6932f01dc..9ce147a3ff 100644 --- a/libs/common/src/tools/rx.spec.ts +++ b/libs/common/src/tools/rx.spec.ts @@ -8,6 +8,7 @@ import { awaitAsync, trackEmissions } from "../../spec"; import { anyComplete, + errorOnChange, distinctIfShallowMatch, on, ready, @@ -15,6 +16,104 @@ import { withLatestReady, } from "./rx"; +describe("errorOnChange", () => { + it("emits a single value when the input emits only once", async () => { + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(errorOnChange()).subscribe((v) => results.push(v)); + + source$.next(1); + + expect(results).toEqual([1]); + }); + + it("emits when the input emits", async () => { + const source$ = new Subject(); + const results: number[] = []; + source$.pipe(errorOnChange()).subscribe((v) => results.push(v)); + + source$.next(1); + source$.next(1); + + expect(results).toEqual([1, 1]); + }); + + it("errors when the input errors", async () => { + const source$ = new Subject(); + const expected = {}; + let error: any = null; + source$.pipe(errorOnChange()).subscribe({ error: (v: unknown) => (error = v) }); + + source$.error(expected); + + expect(error).toBe(expected); + }); + + it("completes when the input completes", async () => { + const source$ = new Subject(); + let complete: boolean = false; + source$.pipe(errorOnChange()).subscribe({ complete: () => (complete = true) }); + + source$.complete(); + + expect(complete).toBeTrue(); + }); + + it("errors when the input changes", async () => { + const source$ = new Subject(); + let error: any = null; + source$.pipe(errorOnChange()).subscribe({ error: (v: unknown) => (error = v) }); + + source$.next(1); + source$.next(2); + + expect(error).toEqual({ expectedValue: 1, actualValue: 2 }); + }); + + it("emits when the extracted value remains constant", async () => { + type Foo = { foo: string }; + const source$ = new Subject(); + const results: Foo[] = []; + source$.pipe(errorOnChange((v) => v.foo)).subscribe((v) => results.push(v)); + + source$.next({ foo: "bar" }); + source$.next({ foo: "bar" }); + + expect(results).toEqual([{ foo: "bar" }, { foo: "bar" }]); + }); + + it("errors when an extracted value changes", async () => { + type Foo = { foo: string }; + const source$ = new Subject(); + let error: any = null; + source$.pipe(errorOnChange((v) => v.foo)).subscribe({ error: (v: unknown) => (error = v) }); + + source$.next({ foo: "bar" }); + source$.next({ foo: "baz" }); + + expect(error).toEqual({ expectedValue: "bar", actualValue: "baz" }); + }); + + it("constructs an error when the extracted value changes", async () => { + type Foo = { foo: string }; + const source$ = new Subject(); + let error: any = null; + source$ + .pipe( + errorOnChange( + (v) => v.foo, + (expected, actual) => ({ expected, actual }), + ), + ) + .subscribe({ error: (v: unknown) => (error = v) }); + + source$.next({ foo: "bar" }); + source$.next({ foo: "baz" }); + + expect(error).toEqual({ expected: "bar", actual: "baz" }); + }); +}); + describe("reduceCollection", () => { it.each([[null], [undefined], [[]]])( "should return the default value when the collection is %p", diff --git a/libs/common/src/tools/rx.ts b/libs/common/src/tools/rx.ts index d5d0b499ff..5c4f6a0a70 100644 --- a/libs/common/src/tools/rx.ts +++ b/libs/common/src/tools/rx.ts @@ -15,8 +15,60 @@ import { takeUntil, withLatestFrom, concatMap, + startWith, + pairwise, } from "rxjs"; +/** Returns its input. */ +function identity(value: any): any { + return value; +} + +/** Combines its arguments into a plain old javascript object. */ +function expectedAndActualValue(expectedValue: any, actualValue: any) { + return { + expectedValue, + actualValue, + }; +} + +/** + * An observable operator that throws an error when the stream's + * value changes. Uses strict (`===`) comparison checks. + * @param extract a function that identifies the member to compare; + * defaults to the identity function + * @param error a function that packages the expected and failed + * values into an error. + * @returns a stream of values that emits when the input emits, + * completes when the input completes, and errors when either the + * input errors or the comparison fails. + */ +export function errorOnChange( + extract: (value: Input) => Extracted = identity, + error: (expectedValue: Extracted, actualValue: Extracted) => unknown = expectedAndActualValue, +): OperatorFunction { + return pipe( + startWith(null), + pairwise(), + map(([expected, actual], i) => { + // always let the first value through + if (i === 0) { + return actual; + } + + const expectedValue = extract(expected); + const actualValue = extract(actual); + + // fail the stream if the state desyncs from its initial value + if (expectedValue === actualValue) { + return actual; + } else { + throw error(expectedValue, actualValue); + } + }), + ); +} + /** * An observable operator that reduces an emitted collection to a single object, * returning a default if all items are ignored. diff --git a/libs/common/src/tools/state/object-key.ts b/libs/common/src/tools/state/object-key.ts index 0593186ec4..260a2412b2 100644 --- a/libs/common/src/tools/state/object-key.ts +++ b/libs/common/src/tools/state/object-key.ts @@ -5,6 +5,17 @@ import type { StateDefinition } from "../../platform/state/state-definition"; import { ClassifiedFormat } from "./classified-format"; import { Classifier } from "./classifier"; +/** Determines the format of persistent storage. + * `plain` storage is a plain-old javascript object. Use this type + * when you are performing your own encryption and decryption. + * `classified` uses the `ClassifiedFormat` type as its format. + * `secret-state` uses `Array` with a length of 1. + * @remarks - CAUTION! If your on-disk data is not in a correct format, + * the storage system treats the data as corrupt and returns your initial + * value. + */ +export type ObjectStorageFormat = "plain" | "classified" | "secret-state"; + /** A key for storing JavaScript objects (`{ an: "example" }`) * in a UserStateSubject. */ @@ -20,7 +31,7 @@ export type ObjectKey> key: string; state: StateDefinition; classifier: Classifier; - format: "plain" | "classified"; + format: ObjectStorageFormat; options: UserKeyDefinitionOptions; initial?: State; }; @@ -47,6 +58,18 @@ export function toUserKeyDefinition( }, ); + return classified; + } else if (key.format === "secret-state") { + const classified = new UserKeyDefinition<[ClassifiedFormat]>( + key.state, + key.key, + { + cleanupDelayMs: key.options.cleanupDelayMs, + deserializer: (jsonValue) => jsonValue as [ClassifiedFormat], + clearOn: key.options.clearOn, + }, + ); + return classified; } else { throw new Error(`unknown format: ${key.format}`); diff --git a/libs/common/src/tools/state/secret-state.spec.ts b/libs/common/src/tools/state/secret-state.spec.ts index d4727492b3..5f679644fc 100644 --- a/libs/common/src/tools/state/secret-state.spec.ts +++ b/libs/common/src/tools/state/secret-state.spec.ts @@ -11,11 +11,11 @@ import { import { EncString } from "../../platform/models/domain/enc-string"; import { GENERATOR_DISK } from "../../platform/state"; import { UserId } from "../../types/guid"; +import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; import { SecretClassifier } from "./secret-classifier"; import { SecretKeyDefinition } from "./secret-key-definition"; import { SecretState } from "./secret-state"; -import { UserEncryptor } from "./user-encryptor.abstraction"; type FooBar = { foo: boolean; bar: boolean; date?: Date }; const classifier = SecretClassifier.allSecret(); diff --git a/libs/common/src/tools/state/secret-state.ts b/libs/common/src/tools/state/secret-state.ts index 45ce855cc8..fe7c025ccf 100644 --- a/libs/common/src/tools/state/secret-state.ts +++ b/libs/common/src/tools/state/secret-state.ts @@ -8,10 +8,10 @@ import { CombinedState, } from "../../platform/state"; import { UserId } from "../../types/guid"; +import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; import { ClassifiedFormat } from "./classified-format"; import { SecretKeyDefinition } from "./secret-key-definition"; -import { UserEncryptor } from "./user-encryptor.abstraction"; const ONE_MINUTE = 1000 * 60; diff --git a/libs/common/src/tools/state/user-state-subject.spec.ts b/libs/common/src/tools/state/user-state-subject.spec.ts index ee78a5c048..6a50a1dd66 100644 --- a/libs/common/src/tools/state/user-state-subject.spec.ts +++ b/libs/common/src/tools/state/user-state-subject.spec.ts @@ -4,13 +4,13 @@ import { GENERATOR_DISK, UserKeyDefinition } from "@bitwarden/common/platform/st import { UserId } from "@bitwarden/common/types/guid"; import { awaitAsync, FakeSingleUserState, ObservableTracker } from "../../../spec"; +import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; import { UserBound } from "../dependencies"; import { PrivateClassifier } from "../private-classifier"; import { StateConstraints } from "../types"; import { ClassifiedFormat } from "./classified-format"; import { ObjectKey } from "./object-key"; -import { UserEncryptor } from "./user-encryptor.abstraction"; import { UserStateSubject } from "./user-state-subject"; const SomeUser = "some user" as UserId; @@ -734,6 +734,7 @@ describe("UserStateSubject", () => { error = e as any; }, }); + singleUserEncryptor$.next({ userId: SomeUser, encryptor: SomeEncryptor }); singleUserEncryptor$.next({ userId: errorUserId, encryptor: SomeEncryptor }); await awaitAsync(); diff --git a/libs/common/src/tools/state/user-state-subject.ts b/libs/common/src/tools/state/user-state-subject.ts index 0b562cc7a1..4a2dab1234 100644 --- a/libs/common/src/tools/state/user-state-subject.ts +++ b/libs/common/src/tools/state/user-state-subject.ts @@ -6,10 +6,8 @@ import { filter, map, takeUntil, - pairwise, distinctUntilChanged, BehaviorSubject, - startWith, Observable, Subscription, last, @@ -30,15 +28,15 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SingleUserState, UserKeyDefinition } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; +import { UserEncryptor } from "../cryptography/user-encryptor.abstraction"; import { UserBound } from "../dependencies"; -import { anyComplete, ready, withLatestReady } from "../rx"; +import { anyComplete, errorOnChange, ready, withLatestReady } from "../rx"; import { Constraints, SubjectConstraints, WithConstraints } from "../types"; import { ClassifiedFormat, isClassifiedFormat } from "./classified-format"; import { unconstrained$ } from "./identity-state-constraint"; import { isObjectKey, ObjectKey, toUserKeyDefinition } from "./object-key"; import { isDynamic } from "./state-constraints-dependency"; -import { UserEncryptor } from "./user-encryptor.abstraction"; import { UserStateSubjectDependencies } from "./user-state-subject-dependencies"; type Constrained = { constraints: Readonly>; state: State }; @@ -195,24 +193,13 @@ export class UserStateSubject< } }), // fail the stream if the state desyncs from the bound userId - startWith({ userId: this.state.userId, encryptor: null } as UserBound< - "encryptor", - UserEncryptor - >), - pairwise(), - map(([expected, actual]) => { - if (expected.userId === actual.userId) { - return actual; - } else { - throw { - expectedUserId: expected.userId, - actualUserId: actual.userId, - }; - } - }), + errorOnChange( + ({ userId }) => userId, + (expectedUserId, actualUserId) => ({ expectedUserId, actualUserId }), + ), // reduce emissions to when encryptor changes - distinctUntilChanged(), map(({ encryptor }) => encryptor), + distinctUntilChanged(), ); } @@ -317,36 +304,63 @@ export class UserStateSubject< return (input$) => input$ as Observable; } - // if the key supports encryption, enable encryptor support + // all other keys support encryption; enable encryptor support + return pipe( + this.mapToClassifiedFormat(), + combineLatestWith(encryptor$), + concatMap(async ([input, encryptor]) => { + // pass through null values + if (input === null || input === undefined) { + return null; + } + + // decrypt classified data + const { secret, disclosed } = input; + const encrypted = EncString.fromJSON(secret); + const decryptedSecret = await encryptor.decrypt(encrypted); + + // assemble into proper state + const declassified = this.objectKey.classifier.declassify(disclosed, decryptedSecret); + const state = this.objectKey.options.deserializer(declassified); + + return state; + }), + ); + } + + private mapToClassifiedFormat(): OperatorFunction> { + // FIXME: warn when data is dropped in the console and/or report an error + // through the observable; consider redirecting dropped data to a recovery + // location + + // user-state subject's default format is object-aware if (this.objectKey && this.objectKey.format === "classified") { - return pipe( - combineLatestWith(encryptor$), - concatMap(async ([input, encryptor]) => { - // pass through null values - if (input === null || input === undefined) { - return null; - } + return map((input) => { + if (!isClassifiedFormat(input)) { + return null; + } - // fail fast if the format is incorrect - if (!isClassifiedFormat(input)) { - throw new Error(`Cannot declassify ${this.key.key}; unknown format.`); - } - - // decrypt classified data - const { secret, disclosed } = input; - const encrypted = EncString.fromJSON(secret); - const decryptedSecret = await encryptor.decrypt(encrypted); - - // assemble into proper state - const declassified = this.objectKey.classifier.declassify(disclosed, decryptedSecret); - const state = this.objectKey.options.deserializer(declassified); - - return state; - }), - ); + return input; + }); } - throw new Error(`unknown serialization format: ${this.objectKey.format}`); + // secret state's format wraps objects in an array + if (this.objectKey && this.objectKey.format === "secret-state") { + return map((input) => { + if (!Array.isArray(input)) { + return null; + } + + const [unwrapped] = input; + if (!isClassifiedFormat(unwrapped)) { + return null; + } + + return unwrapped; + }); + } + + throw new Error(`unsupported serialization format: ${this.objectKey.format}`); } private classify(encryptor$: Observable): OperatorFunction { @@ -359,41 +373,49 @@ export class UserStateSubject< ); } - // if the key supports encryption, enable encryptor support + // all other keys support encryption; enable encryptor support + return pipe( + withLatestReady(encryptor$), + concatMap(async ([input, encryptor]) => { + // fail fast if there's no value + if (input === null || input === undefined) { + return null; + } + + // split data by classification level + const serialized = JSON.parse(JSON.stringify(input)); + const classified = this.objectKey.classifier.classify(serialized); + + // protect data + const encrypted = await encryptor.encrypt(classified.secret); + const secret = JSON.parse(JSON.stringify(encrypted)); + + // wrap result in classified format envelope for storage + const envelope = { + id: null as void, + secret, + disclosed: classified.disclosed, + } satisfies ClassifiedFormat; + + // deliberate type erasure; the type is restored during `declassify` + return envelope as ClassifiedFormat; + }), + this.mapToStorageFormat(), + ); + } + + private mapToStorageFormat(): OperatorFunction, unknown> { + // user-state subject's default format is object-aware if (this.objectKey && this.objectKey.format === "classified") { - return pipe( - withLatestReady(encryptor$), - concatMap(async ([input, encryptor]) => { - // fail fast if there's no value - if (input === null || input === undefined) { - return null; - } - - // split data by classification level - const serialized = JSON.parse(JSON.stringify(input)); - const classified = this.objectKey.classifier.classify(serialized); - - // protect data - const encrypted = await encryptor.encrypt(classified.secret); - const secret = JSON.parse(JSON.stringify(encrypted)); - - // wrap result in classified format envelope for storage - const envelope = { - id: null as void, - secret, - disclosed: classified.disclosed, - } satisfies ClassifiedFormat; - - // deliberate type erasure; the type is restored during `declassify` - return envelope as unknown; - }), - ); + return map((input) => input as unknown); } - // FIXME: add "encrypted" format --> key contains encryption logic - // CONSIDER: should "classified format" algorithm be embedded in subject keys...? + // secret state's format wraps objects in an array + if (this.objectKey && this.objectKey.format === "secret-state") { + return map((input) => [input] as unknown); + } - throw new Error(`unknown serialization format: ${this.objectKey.format}`); + throw new Error(`unsupported serialization format: ${this.objectKey.format}`); } /** The userId to which the subject is bound. diff --git a/libs/components/src/select/index.ts b/libs/components/src/select/index.ts index faebfee9bb..8ef9993efd 100644 --- a/libs/components/src/select/index.ts +++ b/libs/components/src/select/index.ts @@ -1,3 +1,4 @@ export * from "./select.module"; export * from "./select.component"; +export * from "./option"; export * from "./option.component"; diff --git a/libs/components/src/toggle-group/toggle.component.html b/libs/components/src/toggle-group/toggle.component.html index c245d2f462..d036b1751b 100644 --- a/libs/components/src/toggle-group/toggle.component.html +++ b/libs/components/src/toggle-group/toggle.component.html @@ -6,7 +6,7 @@ [checked]="selected" (change)="onInputInteraction()" /> - + diff --git a/libs/components/src/toggle-group/toggle.component.ts b/libs/components/src/toggle-group/toggle.component.ts index 795683ed1c..91096b3829 100644 --- a/libs/components/src/toggle-group/toggle.component.ts +++ b/libs/components/src/toggle-group/toggle.component.ts @@ -1,5 +1,6 @@ import { AfterContentChecked, + AfterViewInit, Component, ElementRef, HostBinding, @@ -17,7 +18,7 @@ let nextId = 0; templateUrl: "./toggle.component.html", preserveWhitespaces: false, }) -export class ToggleComponent implements AfterContentChecked { +export class ToggleComponent implements AfterContentChecked, AfterViewInit { id = nextId++; @Input() value?: TValue; @@ -30,6 +31,7 @@ export class ToggleComponent implements AfterContentChecked { @HostBinding("class") classList = ["tw-group/toggle", "tw-flex", "tw-min-w-16"]; protected bitBadgeContainerHasChidlren = signal(false); + protected labelTitle = signal(null); get name() { return this.groupComponent.name; @@ -43,10 +45,6 @@ export class ToggleComponent implements AfterContentChecked { return ["tw-peer/toggle-input", "tw-appearance-none", "tw-outline-none"]; } - get labelTextContent() { - return this.labelContent?.nativeElement.innerText ?? null; - } - get labelClasses() { return [ "tw-h-full", @@ -102,4 +100,11 @@ export class ToggleComponent implements AfterContentChecked { this.bitBadgeContainer?.nativeElement.childElementCount > 0, ); } + + ngAfterViewInit() { + const labelText = this.labelContent?.nativeElement.innerText; + if (labelText) { + this.labelTitle.set(labelText); + } + } } diff --git a/libs/importer/src/components/dialog/index.ts b/libs/importer/src/components/dialog/index.ts index 641cd6600a..a115426eea 100644 --- a/libs/importer/src/components/dialog/index.ts +++ b/libs/importer/src/components/dialog/index.ts @@ -1,3 +1,4 @@ export * from "./import-error-dialog.component"; export * from "./import-success-dialog.component"; export * from "./file-password-prompt.component"; +export * from "./sshkey-password-prompt.component"; diff --git a/libs/importer/src/components/dialog/sshkey-password-prompt.component.html b/libs/importer/src/components/dialog/sshkey-password-prompt.component.html new file mode 100644 index 0000000000..a42615b1cd --- /dev/null +++ b/libs/importer/src/components/dialog/sshkey-password-prompt.component.html @@ -0,0 +1,31 @@ + + + + {{ "enterSshKeyPassword" | i18n }} + + + + {{ "enterSshKeyPasswordDesc" | i18n }} + + {{ "confirmSshKeyPassword" | i18n }} + + + + + + + + {{ "importSshKey" | i18n }} + + + {{ "cancel" | i18n }} + + + + diff --git a/libs/importer/src/components/dialog/sshkey-password-prompt.component.ts b/libs/importer/src/components/dialog/sshkey-password-prompt.component.ts new file mode 100644 index 0000000000..527dfec6e8 --- /dev/null +++ b/libs/importer/src/components/dialog/sshkey-password-prompt.component.ts @@ -0,0 +1,46 @@ +import { DialogRef } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; +import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { + AsyncActionsModule, + ButtonModule, + DialogModule, + FormFieldModule, + IconButtonModule, +} from "@bitwarden/components"; + +@Component({ + templateUrl: "sshkey-password-prompt.component.html", + standalone: true, + imports: [ + CommonModule, + JslibModule, + DialogModule, + FormFieldModule, + AsyncActionsModule, + ButtonModule, + IconButtonModule, + ReactiveFormsModule, + ], +}) +export class SshKeyPasswordPromptComponent { + protected formGroup = this.formBuilder.group({ + sshKeyPassword: ["", Validators.required], + }); + + constructor( + public dialogRef: DialogRef, + protected formBuilder: FormBuilder, + ) {} + + submit = () => { + this.formGroup.markAsTouched(); + if (!this.formGroup.valid) { + return; + } + this.dialogRef.close(this.formGroup.value.sshKeyPassword); + }; +} diff --git a/libs/tools/generator/components/src/forwarder-settings.component.html b/libs/tools/generator/components/src/forwarder-settings.component.html index 0e15c2e89a..d610f53d59 100644 --- a/libs/tools/generator/components/src/forwarder-settings.component.html +++ b/libs/tools/generator/components/src/forwarder-settings.component.html @@ -12,7 +12,7 @@ {{ "apiKey" | i18n }} - + ("Randomizer"); + +/** Shared module containing generator component dependencies */ +@NgModule({ + imports: [JslibModule], + providers: [ + safeProvider({ + provide: RANDOMIZER, + useFactory: createRandomizer, + deps: [KeyService], + }), + safeProvider({ + provide: LegacyEncryptorProvider, + useClass: KeyServiceLegacyEncryptorProvider, + deps: [EncryptService, KeyService], + }), + safeProvider({ + provide: CredentialGeneratorService, + useClass: CredentialGeneratorService, + deps: [ + RANDOMIZER, + StateProvider, + PolicyService, + ApiService, + I18nService, + LegacyEncryptorProvider, + AccountService, + ], + }), + ], +}) +export class GeneratorServicesModule { + constructor() {} +} diff --git a/libs/tools/generator/components/src/generator.module.ts b/libs/tools/generator/components/src/generator.module.ts index e73d687d7d..f0d09b53eb 100644 --- a/libs/tools/generator/components/src/generator.module.ts +++ b/libs/tools/generator/components/src/generator.module.ts @@ -3,14 +3,6 @@ import { NgModule } from "@angular/core"; import { ReactiveFormsModule } from "@angular/forms"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; -import { SafeInjectionToken } from "@bitwarden/angular/services/injection-tokens"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { StateProvider } from "@bitwarden/common/platform/state"; import { CardComponent, ColorPasswordModule, @@ -25,16 +17,11 @@ import { ToggleGroupModule, TypographyModule, } from "@bitwarden/components"; -import { - createRandomizer, - CredentialGeneratorService, - Randomizer, -} from "@bitwarden/generator-core"; -import { KeyService } from "@bitwarden/key-management"; import { CatchallSettingsComponent } from "./catchall-settings.component"; import { CredentialGeneratorComponent } from "./credential-generator.component"; import { ForwarderSettingsComponent } from "./forwarder-settings.component"; +import { GeneratorServicesModule } from "./generator-services.module"; import { PassphraseSettingsComponent } from "./passphrase-settings.component"; import { PasswordGeneratorComponent } from "./password-generator.component"; import { PasswordSettingsComponent } from "./password-settings.component"; @@ -42,8 +29,6 @@ import { SubaddressSettingsComponent } from "./subaddress-settings.component"; import { UsernameGeneratorComponent } from "./username-generator.component"; import { UsernameSettingsComponent } from "./username-settings.component"; -const RANDOMIZER = new SafeInjectionToken("Randomizer"); - /** Shared module containing generator component dependencies */ @NgModule({ imports: [ @@ -52,6 +37,7 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer"); CheckboxModule, CommonModule, FormFieldModule, + GeneratorServicesModule, IconButtonModule, InputModule, ItemModule, @@ -63,27 +49,6 @@ const RANDOMIZER = new SafeInjectionToken("Randomizer"); ToggleGroupModule, TypographyModule, ], - providers: [ - safeProvider({ - provide: RANDOMIZER, - useFactory: createRandomizer, - deps: [KeyService], - }), - safeProvider({ - provide: CredentialGeneratorService, - useClass: CredentialGeneratorService, - deps: [ - RANDOMIZER, - StateProvider, - PolicyService, - ApiService, - I18nService, - EncryptService, - KeyService, - AccountService, - ], - }), - ], declarations: [ CatchallSettingsComponent, CredentialGeneratorComponent, diff --git a/libs/tools/generator/components/src/index.ts b/libs/tools/generator/components/src/index.ts index 3850e8a3be..56eb912f36 100644 --- a/libs/tools/generator/components/src/index.ts +++ b/libs/tools/generator/components/src/index.ts @@ -2,3 +2,4 @@ export { CredentialGeneratorHistoryComponent } from "./credential-generator-hist export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component"; export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component"; export { GeneratorModule } from "./generator.module"; +export { GeneratorServicesModule } from "./generator-services.module"; diff --git a/libs/tools/generator/core/src/data/generators.ts b/libs/tools/generator/core/src/data/generators.ts index 5ee451bfc5..7fb5e4d78d 100644 --- a/libs/tools/generator/core/src/data/generators.ts +++ b/libs/tools/generator/core/src/data/generators.ts @@ -369,7 +369,7 @@ export function toCredentialGeneratorConfiguration; const defaultSettings = Object.freeze({ token: "", domain: "", + baseUrl: "", }); // supported RPC calls @@ -65,9 +66,10 @@ const forwarder = Object.freeze({ // e.g. key: "forwarder.AddyIo.local.settings", key: "addyIoForwarder", target: "object", - format: "classified", + format: "secret-state", classifier: new PrivateClassifier(), state: GENERATOR_DISK, + initial: defaultSettings, options: { deserializer: (value) => value, clearOn: ["logout"], diff --git a/libs/tools/generator/core/src/integration/duck-duck-go.ts b/libs/tools/generator/core/src/integration/duck-duck-go.ts index 4c1d672cc6..56d6b79687 100644 --- a/libs/tools/generator/core/src/integration/duck-duck-go.ts +++ b/libs/tools/generator/core/src/integration/duck-duck-go.ts @@ -55,9 +55,10 @@ const forwarder = Object.freeze({ // e.g. key: "forwarder.DuckDuckGo.local.settings", key: "duckDuckGoForwarder", target: "object", - format: "classified", + format: "secret-state", classifier: new PrivateClassifier(), state: GENERATOR_DISK, + initial: defaultSettings, options: { deserializer: (value) => value, clearOn: ["logout"], diff --git a/libs/tools/generator/core/src/integration/fastmail.ts b/libs/tools/generator/core/src/integration/fastmail.ts index 13aa8db624..9c0d927a2f 100644 --- a/libs/tools/generator/core/src/integration/fastmail.ts +++ b/libs/tools/generator/core/src/integration/fastmail.ts @@ -123,9 +123,10 @@ const forwarder = Object.freeze({ // e.g. key: "forwarder.Fastmail.local.settings" key: "fastmailForwarder", target: "object", - format: "classified", + format: "secret-state", classifier: new PrivateClassifier(), state: GENERATOR_DISK, + initial: defaultSettings, options: { deserializer: (value) => value, clearOn: ["logout"], diff --git a/libs/tools/generator/core/src/integration/firefox-relay.ts b/libs/tools/generator/core/src/integration/firefox-relay.ts index 9c965a4c9c..a5e1c2f60b 100644 --- a/libs/tools/generator/core/src/integration/firefox-relay.ts +++ b/libs/tools/generator/core/src/integration/firefox-relay.ts @@ -59,9 +59,10 @@ const forwarder = Object.freeze({ // e.g. key: "forwarder.Firefox.local.settings", key: "firefoxRelayForwarder", target: "object", - format: "classified", + format: "secret-state", classifier: new PrivateClassifier(), state: GENERATOR_DISK, + initial: defaultSettings, options: { deserializer: (value) => value, clearOn: ["logout"], diff --git a/libs/tools/generator/core/src/integration/forward-email.ts b/libs/tools/generator/core/src/integration/forward-email.ts index a128159fcd..9178c5e474 100644 --- a/libs/tools/generator/core/src/integration/forward-email.ts +++ b/libs/tools/generator/core/src/integration/forward-email.ts @@ -62,9 +62,10 @@ const forwarder = Object.freeze({ // e.g. key: "forwarder.ForwardEmail.local.settings", key: "forwardEmailForwarder", target: "object", - format: "classified", + format: "secret-state", classifier: new PrivateClassifier
{{ "deleteOrganizationUserWarning" | i18n }}
{{ "deleteManyOrganizationUsersWarningDesc" | i18n }}
{{ "revokeUsersWarning" | i18n }}
- {{ "removeMembersWithoutMasterPasswordWarning" | i18n }} -
{{ "revokeMembersWarning" | i18n }}
+ {{ "restoreMembersInstructions" | i18n }} +
+ {{ "removeMembersWithoutMasterPasswordWarning" | i18n }} +
+ {{ "authenticationSessionTimedOut" | i18n }} +
{{ fingerprintPhrase }}