From a8d0ca7aafbb76dbb649d00b4287ebd208c8f5b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 07:31:50 +0000 Subject: [PATCH 01/10] Autosync the updated translations (#6523) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 6 ++ apps/web/src/locales/ar/messages.json | 6 ++ apps/web/src/locales/az/messages.json | 10 +- apps/web/src/locales/be/messages.json | 8 +- apps/web/src/locales/bg/messages.json | 6 ++ apps/web/src/locales/bn/messages.json | 6 ++ apps/web/src/locales/bs/messages.json | 6 ++ apps/web/src/locales/ca/messages.json | 6 ++ apps/web/src/locales/cs/messages.json | 6 ++ apps/web/src/locales/cy/messages.json | 6 ++ apps/web/src/locales/da/messages.json | 6 ++ apps/web/src/locales/de/messages.json | 6 ++ apps/web/src/locales/el/messages.json | 6 ++ apps/web/src/locales/en_GB/messages.json | 6 ++ apps/web/src/locales/en_IN/messages.json | 6 ++ apps/web/src/locales/eo/messages.json | 6 ++ apps/web/src/locales/es/messages.json | 6 ++ apps/web/src/locales/et/messages.json | 6 ++ apps/web/src/locales/eu/messages.json | 6 ++ apps/web/src/locales/fa/messages.json | 116 ++++++++++++----------- apps/web/src/locales/fi/messages.json | 14 ++- apps/web/src/locales/fil/messages.json | 6 ++ apps/web/src/locales/fr/messages.json | 8 +- apps/web/src/locales/gl/messages.json | 6 ++ apps/web/src/locales/he/messages.json | 6 ++ apps/web/src/locales/hi/messages.json | 6 ++ apps/web/src/locales/hr/messages.json | 6 ++ apps/web/src/locales/hu/messages.json | 6 ++ apps/web/src/locales/id/messages.json | 6 ++ apps/web/src/locales/it/messages.json | 6 ++ apps/web/src/locales/ja/messages.json | 6 ++ apps/web/src/locales/ka/messages.json | 6 ++ apps/web/src/locales/km/messages.json | 6 ++ apps/web/src/locales/kn/messages.json | 6 ++ apps/web/src/locales/ko/messages.json | 6 ++ apps/web/src/locales/lv/messages.json | 6 ++ apps/web/src/locales/ml/messages.json | 6 ++ apps/web/src/locales/mr/messages.json | 6 ++ apps/web/src/locales/my/messages.json | 6 ++ apps/web/src/locales/nb/messages.json | 6 ++ apps/web/src/locales/ne/messages.json | 6 ++ apps/web/src/locales/nl/messages.json | 6 ++ apps/web/src/locales/nn/messages.json | 6 ++ apps/web/src/locales/or/messages.json | 6 ++ apps/web/src/locales/pl/messages.json | 6 ++ apps/web/src/locales/pt_BR/messages.json | 6 ++ apps/web/src/locales/pt_PT/messages.json | 6 ++ apps/web/src/locales/ro/messages.json | 6 ++ apps/web/src/locales/ru/messages.json | 6 ++ apps/web/src/locales/si/messages.json | 6 ++ apps/web/src/locales/sk/messages.json | 6 ++ apps/web/src/locales/sl/messages.json | 6 ++ apps/web/src/locales/sr/messages.json | 10 +- apps/web/src/locales/sr_CS/messages.json | 6 ++ apps/web/src/locales/sv/messages.json | 6 ++ apps/web/src/locales/te/messages.json | 6 ++ apps/web/src/locales/th/messages.json | 6 ++ apps/web/src/locales/tr/messages.json | 6 ++ apps/web/src/locales/uk/messages.json | 10 +- apps/web/src/locales/vi/messages.json | 6 ++ apps/web/src/locales/zh_CN/messages.json | 16 +++- apps/web/src/locales/zh_TW/messages.json | 6 ++ 62 files changed, 444 insertions(+), 72 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 0f57c449591..8eaf6e1f011 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index b21a4e49d18..cd5c185c172 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 151d237b91e..1822746405e 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -7170,12 +7170,18 @@ "message": "Beta" }, "baseUrl": { - "message": "Server URL" + "message": "Server URL-si" }, "aliasDomain": { - "message": "Alias domain" + "message": "Domen ləqəbi" }, "alreadyHaveAccount": { "message": "Artıq bir hesabınız var?" + }, + "customBillingStart": { + "message": "Özəl faktura əks olunmur. Son faktura üçün " + }, + "customBillingEnd": { + "message": " səhifəni ziyarət edin." } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 8ead5f134b1..fbefebf78a1 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -226,7 +226,7 @@ "message": "Праверце, ці не скампраметаваны пароль." }, "passwordExposed": { - "message": "Гэты пароль быў скампраметаваны наступную колькасць разоў: $VALUE$. Вы павінны змяніць яго.", + "message": "Гэты пароль быў скампраметаваны наступную колькасць раз(оў): $VALUE$. Вы павінны змяніць яго.", "placeholders": { "value": { "content": "$1", @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Ужо маеце ўліковы запіс?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index f8c2bd5c15b..f6036998998 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Вече имате регистрация?" + }, + "customBillingStart": { + "message": "Персонализираното таксуване не е отразено. Посетете страницата за " + }, + "customBillingEnd": { + "message": " за последните фактури." } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 2d5a3f43794..25f2a837f52 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 220ad900cc8..7fa48aeaa5e 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index a2276a8443c..30ee2f589f3 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Ja tens un compte?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 1ca1c246d2f..24e3817f615 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Už máte účet?" + }, + "customBillingStart": { + "message": "Vlastní fakturace není zahrnuta. Navštivte stránku " + }, + "customBillingEnd": { + "message": " pro nejnovější faktury." } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index ca615764d5c..8de532e7e26 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Har allerede en konto?" + }, + "customBillingStart": { + "message": "Tilpasset fakturering vises ikke. Besøg siden " + }, + "customBillingEnd": { + "message": " for seneste fakturering." } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index f48baa55bcf..09c18f83434 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Hast du bereits ein Konto?" + }, + "customBillingStart": { + "message": "Eine individuelle Abrechnung wird nicht berücksichtigt. Besuche die " + }, + "customBillingEnd": { + "message": " Seite für die aktuelle Rechnung." } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index b146e47ddc5..5004408eafa 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 0810036fc16..be316df1621 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 3df21cfb599..277641ca9a0 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index b96027b9d4e..77407c53c8f 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index f313f9ba16d..18d85d28661 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "¿Ya tienes una cuenta?" + }, + "customBillingStart": { + "message": "Facturación personalizada no reflejada. Visite el " + }, + "customBillingEnd": { + "message": " para la última facturación." } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 1a56831f0fd..97f5c17bf81 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index e50570a9b68..92d3823cd1e 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 51e292d2d9e..e564086f871 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -709,7 +709,7 @@ "message": "یک خطای غیر منتظره رخ داده است." }, "expirationDateError": { - "message": "Please select an expiration date that is in the future." + "message": "لطفاً تاریخ انقضایی را انتخاب کنید که در آینده است." }, "emailAddress": { "message": "نشانی ایمیل" @@ -952,7 +952,7 @@ "message": "کپی کد تأیید" }, "copyUuid": { - "message": "Copy UUID" + "message": "کپی UUID" }, "warning": { "message": "هشدار" @@ -1291,19 +1291,19 @@ "message": "خطا در رمزگشایی پرونده‌ی درون ریزی شده. کلید رمزگذاری شما با کلید رمزگذاری استفاده شده برای درون ریزی داده‌ها مطابقت ندارد." }, "importDestination": { - "message": "Import destination" + "message": "مقصد برون ریزی" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "درباره گزینه‌های برون ریزی خود بیاموزید" }, "selectImportFolder": { - "message": "Select a folder" + "message": "یک پوشه انتخاب کنید" }, "selectImportCollection": { - "message": "Select a collection" + "message": "انتخاب یک مجموعه" }, "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "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": { @@ -1313,7 +1313,7 @@ } }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "فایل حاوی موارد اختصاص نیافته است." }, "selectFormat": { "message": "فرمت پرونده‌ی درون ریزی را انتخاب کنید" @@ -1423,10 +1423,10 @@ "message": "با درخواست یک مرحله اضافی هنگام ورود، حساب خود را ایمن کنید." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "ورود دو مرحله ای را برای سازمان خود فعال کنید." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "گزینه‌های ورود دو مرحله ای Bitwarden را برای اعضا اعمال کنید با استفاده از ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -1925,7 +1925,7 @@ "message": "۱ گیگابایت فضای ذخیره‌سازی رمزنگاری شده برای پرونده‌های پیوست." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "گزینه های ورود اضافی دو مرحله ای مانند YubiKey و Duo." }, "premiumSignUpEmergency": { "message": "دسترسی اضطراری" @@ -2041,7 +2041,7 @@ } }, "paymentChargedWithUnpaidSubscription": { - "message": "Your payment method will be charged for any unpaid subscriptions." + "message": "روش پرداخت شما برای هرگونه اشتراک پرداخت نشده شارژ خواهد شد." }, "paymentChargedWithTrial": { "message": "طرح شما با یک دوره آزمایشی رایگان ۷ روزه همراه است. تا پایان دوره آزمایشی از روش پرداخت شما هزینه ای کسر نمی‌شود. هر زمان که مایل بودید می‌توانید آن را لغو کنید." @@ -3477,7 +3477,7 @@ "message": "کلید رمزگذاری را به‌روز کنید" }, "updateEncryptionSchemeDesc": { - "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." + "message": "ما طرح رمزگذاری را برای ارائه امنیت بهتر تغییر داده‌ایم. کلید رمزگذاری خود را اکنون با وارد کردن کلمه اصلی خود در زیر به روز کنید." }, "updateEncryptionKeyWarning": { "message": "پس از به‌روزرسانی کلید رمزگذاری، باید از سیستم خارج شوید و دوباره به همه برنامه‌های Bitwarden که در حال حاضر استفاده می‌کنید (مانند برنامه تلفن همراه یا برنامه‌های افزودنی مرورگر) وارد شوید. عدم خروج و ورود مجدد (که کلید رمزگذاری جدید شما را دانلود می‌کند) ممکن است منجر به خراب شدن داده‌ها شود. ما سعی خواهیم کرد شما را به طور خودکار از سیستم خارج کنیم، اما ممکن است با تأخیر انجام شود." @@ -3534,7 +3534,7 @@ "message": "انتخاب کنید که گاو‌صندوق شما چه زمانی عمل توقف زمانی گاوصندوق را انجام دهد." }, "vaultTimeoutLogoutDesc": { - "message": "Choose when your vault will be logged out." + "message": "انتخاب کنید که گاوصندوق شما چه زمانی از سیستم خارج می‌شود." }, "oneMinute": { "message": "۱ دقیقه" @@ -3625,7 +3625,7 @@ "message": "این مورد دارای پرونده های پیوست قدیمی است که باید اصلاح شوند." }, "attachmentFixDescription": { - "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." + "message": "این پیوست از رمزگذاری قدیمی استفاده می‌کند. برای دانلود، رمزگذاری مجدد، و بارگذاری مجدد پیوست، «اصلاح» را انتخاب کنید." }, "fix": { "message": "اصلاح", @@ -4721,7 +4721,7 @@ "message": "خطا" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "مدیر کاربران همچنین باید مجوز مدیریت بازیابی حساب کاربری را داشته باشد" }, "setupProvider": { "message": "راه اندازی ارائه دهنده" @@ -4933,7 +4933,7 @@ "message": "حذف برون ریزی گاوصندوق شخصی" }, "disablePersonalVaultExportDescription": { - "message": "Do not allow members to export data from their individual vault." + "message": "به اعضا اجازه ندهید که داده‌های گاوصندوق شخصی خود را برون ریزی کنند." }, "vaultExportDisabled": { "message": "برون ریزی گاوصندوق غیرفعال شده است" @@ -5437,7 +5437,7 @@ "message": "در حال برون ریزی گاوصندوق سازمان" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "فقط موارد شخصی گاوصندوق مرتبط با $EMAIL$ برون ریزی خواهند شد. موارد گاوصندوق سازمان شامل نخواهد شد. فقط اطلاعات مورد گاوصندوق برون ریزی خواهد شد و شامل تاریخچه کلمه عبور مرتبط یا پیوست نمی‌شود.", "placeholders": { "email": { "content": "$1", @@ -5446,7 +5446,7 @@ } }, "exportingOrganizationVaultDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", + "message": "فقط گاوصدوق سازمان مرتبط با $ORGANIZATION$ برون ریزی خواهد شد. موارد موجود در گاوصندوق‌های فردی یا سایر سازمان‌ها شامل نمی‌شوند.", "placeholders": { "organization": { "content": "$1", @@ -6836,58 +6836,58 @@ "message": "تنظیمات KDF را به‌روز کنید" }, "loginInitiated": { - "message": "Login initiated" + "message": "ورود به سیستم آغاز شد" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "تأیید دستگاه لازم است. یک روش تأیید انتخاب کنید:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "این دستگاه را به خاطر بسپار" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "اگر از دستگاه عمومی استفاده می‌کنید علامت را بردارید" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "تأیید با دستگاه دیگرتان" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "درخواست تأیید مدیر" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "تأیید با کلمه عبور اصلی" }, "trustedDeviceEncryption": { - "message": "Trusted device encryption" + "message": "رمزگذاری دستگاه مورد اعتماد" }, "trustedDevices": { - "message": "Trusted devices" + "message": "دستگاه‌های مورد اعتماد" }, "memberDecryptionOptionTdeDescriptionPartOne": { - "message": "Once authenticated, members will decrypt vault data using a key stored on their device. The", + "message": "پس از احراز هویت، اعضا با استفاده از کلید ذخیره شده در دستگاه خود، داده‌های گاوصندوق را رمزگشایی می‌کنند.", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO Required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkOne": { - "message": "single organization", + "message": "سازمان واحد", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartTwo": { - "message": "policy,", + "message": "سیاست‌ها", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkTwo": { - "message": "SSO required", + "message": "SSO الزامی است", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartThree": { - "message": "policy, and", + "message": "سیاست‌ها و", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkThree": { - "message": "account recovery administration", + "message": "مدیریت بازیابی حساب کاربری", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartFour": { - "message": "policy with automatic enrollment will turn on when this option is used.", + "message": "سیاست‌ها با ثبت نام خودکار زمانی که از این گزینه استفاده می‌شود روشن می‌شود.", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "notFound": { @@ -6978,7 +6978,7 @@ "message": "حذف اعضایی که کلمه عبور اصلی ندارند بدون تعیین کلمه عبور برای آنها ممکن است دسترسی به حساب کامل آن‌ها را محدود کند." }, "approvedAuthRequest": { - "message": "Approved device for $ID$.", + "message": "دستگاه تأیید شده برای $ID$.", "placeholders": { "id": { "content": "$1", @@ -6987,7 +6987,7 @@ } }, "rejectedAuthRequest": { - "message": "Denied device for $ID$.", + "message": "دستگاه رد شده برای $ID$.", "placeholders": { "id": { "content": "$1", @@ -6996,7 +6996,7 @@ } }, "requestedDeviceApproval": { - "message": "Requested device approval." + "message": "تأیید دستگاه درخواست." }, "startYour7DayFreeTrialOfBitwardenFor": { "message": "نسخه آزمایشی رایگان ۷ روزه Bitwarden را با $ORG$ شروع کنید", @@ -7014,28 +7014,28 @@ "message": "پرچم منطقه انتخاب شد" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "حساب کاربری با موفقیت ایجاد شد!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "تأیید مدیر درخواست شد" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "درخواست شما به مدیرتان فرستاده شد." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "به محض تأیید مطلع خواهید شد." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "در ورود مشکلی دارید؟" }, "loginApproved": { - "message": "Login approved" + "message": "ورود تأیید شد" }, "userEmailMissing": { - "message": "User email missing" + "message": "ایمیل کاربر وجود ندارد" }, "deviceTrusted": { - "message": "Device trusted" + "message": "دستگاه مورد اعتماد است" }, "sendsNoItemsTitle": { "message": "ارسال‌های فعالی نیست", @@ -7106,7 +7106,7 @@ "message": "حساب‌های خدمات اضافی" }, "includedServiceAccounts": { - "message": "Your plan comes with $COUNT$ service accounts.", + "message": "طرح شما با حساب خدمات $COUNT$ همراه است.", "placeholders": { "count": { "content": "$1", @@ -7115,7 +7115,7 @@ } }, "addAdditionalServiceAccounts": { - "message": "You can add additional service accounts for $COST$ per month.", + "message": "می‌توانید سرویس اضافی حساب‌ها را با $COST$ در ماه اضافه کنید.", "placeholders": { "cost": { "content": "$1", @@ -7148,10 +7148,10 @@ "message": "حداکثر هزینه حساب خدمات بالقوه" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "وارد شده!" }, "smBetaEndedDesc": { - "message": "The Secrets Manager Beta ended $BETA_ENDING_DATE$. You have $DAYS$ days left to add Secrets Manager to your paid subscription and maintain access to Secrets Manager data. Contact Customer Success to add Secrets Manager to your subscription.", + "message": "بتای مدیر اسرار $BETA_ENDING_DATE$ به پایان رسید. $DAYS$ روز فرصت دارید تا بتا مدیر اسرار را به اشتراک پولی خود اضافه کنید و دسترسی به داده‌های مدیر اسرار را حفظ کنید. برای افزودن مدیر اسرار به اشتراک خود، با پشتیبانی مشتری تماس بگیرید.", "placeholders": { "beta_ending_date": { "content": "$1", @@ -7164,18 +7164,24 @@ } }, "betaEnding": { - "message": "Beta Ending" + "message": "آزمایشی در حال اتمام است" }, "beta": { - "message": "Beta" + "message": "آزمایشی" }, "baseUrl": { - "message": "Server URL" + "message": "نشانی اینترنتی سرور" }, "aliasDomain": { - "message": "Alias domain" + "message": "دامنه مستعار" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "پیشتر حساب کاربری داشته اید؟" + }, + "customBillingStart": { + "message": "صورتحساب سفارشی منعکس نمی‌شود. بازدید کنید " + }, + "customBillingEnd": { + "message": " صفحه برای آخرین صورتحساب" } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 07e3bb4cc8a..bb2d7ae1995 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -1341,7 +1341,7 @@ } }, "options": { - "message": "Asetukset" + "message": "Valinnat" }, "preferences": { "message": "Asetukset" @@ -6591,13 +6591,13 @@ "message": "Tallenna lisenssi" }, "projectPeopleDescription": { - "message": "Myönnä ryhmille tai henkilöille tämän projektin käyttöoikeus." + "message": "Myönnä ryhmille tai henkilöille käyttöoikeus projektiin." }, "projectPeopleSelectHint": { "message": "Syötä tai valitse henkilöitä tai ryhmiä" }, "projectServiceAccountsDescription": { - "message": "Myönnä palvelutileille tämän projektin käyttöoikeus." + "message": "Myönnä palvelutileille käyttöoikeus projektiin." }, "projectServiceAccountsSelectHint": { "message": "Syötä tai valitse palvelutilit" @@ -7164,7 +7164,7 @@ } }, "betaEnding": { - "message": "Beta päättyy" + "message": "Beta on päättynyt" }, "beta": { "message": "Beta" @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Onko sinulla jo tili?" + }, + "customBillingStart": { + "message": "Mukautettu laskutus ei näy. Löydät viimeisimmän laskutuksen " + }, + "customBillingEnd": { + "message": " -sivulta." } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 062195bdaf9..b68a81ca3e7 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index aaa39033815..d02cfef1541 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -2535,7 +2535,7 @@ "message": "L’identifiant externe peut être utilisé comme référence ou pour lier cette ressource à un système externe tel qu’un répertoire utilisateur." }, "nestCollectionUnder": { - "message": "Collection imbriquée dessous" + "message": "Collection imbriquée sous" }, "accessControl": { "message": "Contrôle d’accès" @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Vous avez déjà un compte ?" + }, + "customBillingStart": { + "message": "La facturation personnalisée n'apparaît pas. Visitez la page " + }, + "customBillingEnd": { + "message": " pour la dernière facturation." } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 7fe4fe66a9b..70f447848d9 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 9764f7d39da..b6463a2d7cc 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 1911a71a36e..dbf22380177 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Već imaš račun?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 51ded2ab77c..faa4f61db77 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Van már saját fiók?" + }, + "customBillingStart": { + "message": "Az egyedi számlázás nem jelenik meg. Kerssük fel " + }, + "customBillingEnd": { + "message": " oldalt a legújabb számlázásért." } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 0932b38ef0a..13e5d53c17c 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 1b58ad965e3..c726f0e5182 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Hai già un account?" + }, + "customBillingStart": { + "message": "La fatturazione personalizzata non è riflessa. Visita la " + }, + "customBillingEnd": { + "message": " pagina per l'ultima fatturazione." } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index efc64056030..6ec8f42fdf3 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "既にアカウントをお持ちですか?" + }, + "customBillingStart": { + "message": "カスタム請求は反映されていません。" + }, + "customBillingEnd": { + "message": "のページで最新の請求書をご覧いただけます。" } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 47aa45f2fa1..9bfddfad59e 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 8f210948f70..c349b08791a 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index ddf2a0a702d..6e4a514c11a 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 5f41e5c7ede..fcfc4628e6b 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Jau ir konts?" + }, + "customBillingStart": { + "message": "Pielāgotie norēķini nav atspoguļoti. Jāapmeklē " + }, + "customBillingEnd": { + "message": " sadaļu, lai gūtu ieskatu par jaunākajiem rēķiniem." } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 8d6832cb9a8..c29e0f42fad 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index c24948cacaa..656d48456cc 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index da1681c73aa..15621984c0b 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index a0eb8b17d09..09196ea1c8a 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Aangepaste facturering is niet weergeven. Bezoek de " + }, + "customBillingEnd": { + "message": " pagina voor laatste facturering." } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 6de911d6782..da3d7e5a0c3 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index b21b83ffdb7..f6d8e35497a 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Masz już konto?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 5da497922a6..c5333d785f0 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Já tem uma conta?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 38627692658..32c5276b0a1 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Já tem uma conta?" + }, + "customBillingStart": { + "message": "A faturação personalizada não é refletida. Visite a " + }, + "customBillingEnd": { + "message": " página para a faturação mais recente." } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 8de54c1494b..1df9da3f6bf 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 3feac542568..705198ac753 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Уже зарегистрированы?" + }, + "customBillingStart": { + "message": "Пользовательские счета не отображаются. Посетите страницу " + }, + "customBillingEnd": { + "message": " для получения последней информации о выставлении счетов." } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index be9cbdaff31..efdf9a8c86e 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 56752c2ddb6..08276bda0bc 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Už máte účet?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 62dda84d524..0f5ff850696 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index f037202a975..a1844d86fc8 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -7170,12 +7170,18 @@ "message": "Бета" }, "baseUrl": { - "message": "Server URL" + "message": "УРЛ Сервера" }, "aliasDomain": { - "message": "Alias domain" + "message": "Домен алијаса" }, "alreadyHaveAccount": { "message": "Већ имате налог?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 95a8c7fd421..78ab8fa1c70 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 253bee8631e..c5880efe924 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Har du redan ett konto?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index f620a91ec72..7d1a4bebebd 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 593169a336c..b429ed92dae 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 25285f2c317..627eec657cb 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Zaten hesabınız var mı?" + }, + "customBillingStart": { + "message": "Özel faturalandırma yansıtılmıyor. Son fatura sayfasını " + }, + "customBillingEnd": { + "message": " ziyaret edin." } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index deb7f18773b..29111deeb91 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -7170,12 +7170,18 @@ "message": "Бета" }, "baseUrl": { - "message": "Server URL" + "message": "URL-адреса сервера" }, "aliasDomain": { - "message": "Alias domain" + "message": "Псевдонім домену" }, "alreadyHaveAccount": { "message": "Вже маєте обліковий запис?" + }, + "customBillingStart": { + "message": "Власний рахунок не відображений. Відвідайте " + }, + "customBillingEnd": { + "message": " для отримання останнього рахунку." } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 96eef230907..1483e23a2be 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "Already have an account?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index af2cb944862..7f3fc7f09fa 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -603,13 +603,13 @@ "message": "登录或者创建一个账户来访问您的安全密码库。" }, "loginWithDevice": { - "message": "设备登录" + "message": "使用设备登录" }, "loginWithDeviceEnabledNote": { "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他登录选项吗?" }, "loginWithMasterPassword": { - "message": "主密码登录" + "message": "使用主密码登录" }, "createAccount": { "message": "创建账户" @@ -785,7 +785,7 @@ } }, "enterVerificationCodeApp": { - "message": "请输入验证器应用程序中的 6 位验证码。" + "message": "请输入您的验证器应用中的 6 位数验证码。" }, "enterVerificationCodeEmail": { "message": "请输入发送给电子邮件 $EMAIL$ 的 6 位数验证码。", @@ -1519,7 +1519,7 @@ "message": "密钥" }, "twoStepAuthenticatorEnterCode": { - "message": "输入应用程序中的 6 位验证码" + "message": "输入应用程序中的 6 位数验证码" }, "twoStepAuthenticatorReaddDesc": { "message": "如果您要把它添加到另一个设备,下面是您的验证器应用所需要的二维码(或密钥)。" @@ -1615,7 +1615,7 @@ "message": "输入您希望接收验证码的电子邮件" }, "twoFactorEmailEnterCode": { - "message": "输入电子邮件中的 6 位验证码" + "message": "输入电子邮件中的 6 位数验证码" }, "sendEmail": { "message": "发送电子邮件" @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "已经有一个账户?" + }, + "customBillingStart": { + "message": "未返回自定义费用记录。请访问 " + }, + "customBillingEnd": { + "message": " 页面获取最新的账单。" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 192212eff19..eba26af5767 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -7177,5 +7177,11 @@ }, "alreadyHaveAccount": { "message": "已經有帳戶?" + }, + "customBillingStart": { + "message": "Custom billing is not reflected. Visit the " + }, + "customBillingEnd": { + "message": " page for latest invoicing." } } From e1203c2d815af4f77ae2d1d6724dabd46b05988f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 07:32:53 +0000 Subject: [PATCH 02/10] Autosync the updated translations (#6521) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/fa/messages.json | 88 ++++++++++---------- apps/desktop/src/locales/sr/messages.json | 2 +- apps/desktop/src/locales/uk/messages.json | 2 +- apps/desktop/src/locales/zh_CN/messages.json | 4 +- apps/desktop/src/locales/zh_TW/messages.json | 2 +- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index dad420ca1d1..363159ecbdd 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -476,7 +476,7 @@ "message": "بیشترین حجم پرونده ۵۰۰ مگابایت است." }, "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "message": "انتقال کلید رمزگذاری مورد نیاز است. لطفاً از طریق گاوصندوق وب وارد شوید تا کلید رمزگذاری خود را به روز کنید." }, "editedFolder": { "message": "پوشه ذخیره شد" @@ -1078,7 +1078,7 @@ "message": "۱ گیگابایت فضای ذخیره‌سازی رمزنگاری شده برای پرونده‌های پیوست." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "گزینه های ورود اضافی دو مرحله ای مانند YubiKey و Duo." }, "premiumSignUpReports": { "message": "گزارش‌های بهداشت رمز عبور، سلامت حساب و نقض داده‌ها برای ایمن نگهداشتن گاوصندوق شما." @@ -1493,7 +1493,7 @@ "message": "یک گاوصندوق خارج شده درخواست احراز هویت مجدد را برای دسترسی آن می‌دهد." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "یک روش بازگشایی برای پایان زمان مجاز تنظیم کنید." }, "lock": { "message": "قفل", @@ -1985,7 +1985,7 @@ "message": "برون ریزی گاو‌صندوق شخصی" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "فقط موارد شخصی گاوصندوق مرتبط با $EMAIL$ برون ریزی خواهند شد. موارد گاوصندوق سازمان شامل نخواهد شد. فقط اطلاعات مورد گاوصندوق برون ریزی خواهد شد و شامل تاریخچه کلمه عبور مرتبط یا پیوست نمی‌شود.", "placeholders": { "email": { "content": "$1", @@ -2110,7 +2110,7 @@ "message": "با دستگاه دیگری وارد شوید" }, "loginInitiated": { - "message": "Login initiated" + "message": "ورود به سیستم آغاز شد" }, "notificationSentDevice": { "message": "یک اعلان به دستگاه شما ارسال شده است." @@ -2250,35 +2250,35 @@ "message": "به‌روز رسانی تنظیمات توصیه شده" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "تأیید دستگاه لازم است. یک روش تأیید انتخاب کنید:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "این دستگاه را به خاطر بسپار" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "اگر از دستگاه عمومی استفاده می‌کنید علامت را بردارید" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "تأیید با دستگاه دیگرتان" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "درخواست تأیید مدیر" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "تأیید با کلمه عبور اصلی" }, "region": { - "message": "Region" + "message": "منطقه" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "شناسه سازمان SSO مورد نیاز است." }, "eu": { - "message": "EU", + "message": "اروپا", "description": "European Union" }, "loggingInOn": { - "message": "Logging in on" + "message": "ورود با" }, "usDomain": { "message": "bitwarden.com" @@ -2287,46 +2287,46 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "خود میزبان" }, "accessDenied": { "message": "دسترسی رد شد. شما اجازه مشاهده این صفحه را ندارید." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "حساب کاربری با موفقیت ایجاد شد!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "تأیید مدیر درخواست شد" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "درخواست شما به مدیرتان فرستاده شد." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "به محض تأیید مطلع خواهید شد." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "در ورود مشکلی دارید؟" }, "loginApproved": { - "message": "Login approved" + "message": "ورود تأیید شد" }, "userEmailMissing": { - "message": "User email missing" + "message": "ایمیل کاربر وجود ندارد" }, "deviceTrusted": { - "message": "Device trusted" + "message": "دستگاه مورد اعتماد است" }, "inputRequired": { - "message": "Input is required." + "message": "ورودی ضروری است." }, "required": { - "message": "required" + "message": "ضروری" }, "search": { - "message": "Search" + "message": "جستجو" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "ورودی باید حداقل $COUNT$ کاراکتر داشته باشد.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "طول ورودی نباید بیش از $COUNT$ کاراکتر باشد.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "کاراکترهای زیر مجاز نیستند: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "مقدار ورودی باید حداقل $MIN$ باشد.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "مقدار ورودی نباید از $MAX$ تجاوز کند.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "یک یا چند ایمیل نامعتبر است" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "ورودی نباید فقط حاوی فضای خالی باشد.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "ورودی یک نشانی ایمیل نیست." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "فیلد $COUNT$ در بالا به توجه شما نیاز دارد.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- انتخاب --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- برای فیلتر تایپ کنید --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "در حال بازیابی گزینه‌ها..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "موردی یافت نشد" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "پاک‌کردن همه" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ بیشتر", "placeholders": { "quantity": { "content": "$1", @@ -2414,9 +2414,9 @@ } }, "submenu": { - "message": "Submenu" + "message": "زیرمنو" }, "aliasDomain": { - "message": "Alias domain" + "message": "دامنه مستعار" } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 7ce3fe92ab4..392dbe4a141 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -2417,6 +2417,6 @@ "message": "Под-мени" }, "aliasDomain": { - "message": "Alias domain" + "message": "Домен алијаса" } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 61e50fb0e44..a3489dce44e 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -2417,6 +2417,6 @@ "message": "Підменю" }, "aliasDomain": { - "message": "Alias domain" + "message": "Псевдонім домену" } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 2e24b4d9080..f656607fe60 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -594,7 +594,7 @@ "message": "继续" }, "enterVerificationCodeApp": { - "message": "请输入您的身份验证器应用中的 6 位验证码。" + "message": "请输入您的验证器应用中的 6 位数验证码。" }, "enterVerificationCodeEmail": { "message": "请输入发送给电子邮件 $EMAIL$ 的 6 位数验证码。", @@ -2083,7 +2083,7 @@ "message": "密码库" }, "loginWithMasterPassword": { - "message": "主密码登录" + "message": "使用主密码登录" }, "loggingInAs": { "message": "正登录为" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 819e81b91f5..3e8cf00867b 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -2119,7 +2119,7 @@ "message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device." }, "fingerprintPhraseHeader": { - "message": "Fingerprint phrase" + "message": "指紋短語" }, "needAnotherOption": { "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" From 437f39d4fbc24c8d61df0b425c768e1e828d101e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 07:37:02 +0000 Subject: [PATCH 03/10] Autosync the updated translations (#6522) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 24 +++++ apps/browser/src/_locales/az/messages.json | 44 +++++++-- apps/browser/src/_locales/be/messages.json | 24 +++++ apps/browser/src/_locales/bg/messages.json | 50 +++++++--- apps/browser/src/_locales/bn/messages.json | 24 +++++ apps/browser/src/_locales/bs/messages.json | 24 +++++ apps/browser/src/_locales/ca/messages.json | 24 +++++ apps/browser/src/_locales/cs/messages.json | 24 +++++ apps/browser/src/_locales/cy/messages.json | 24 +++++ apps/browser/src/_locales/da/messages.json | 24 +++++ apps/browser/src/_locales/de/messages.json | 24 +++++ apps/browser/src/_locales/el/messages.json | 24 +++++ apps/browser/src/_locales/en_GB/messages.json | 24 +++++ apps/browser/src/_locales/en_IN/messages.json | 24 +++++ apps/browser/src/_locales/es/messages.json | 24 +++++ apps/browser/src/_locales/et/messages.json | 24 +++++ apps/browser/src/_locales/eu/messages.json | 24 +++++ apps/browser/src/_locales/fa/messages.json | 94 ++++++++++++------- apps/browser/src/_locales/fi/messages.json | 24 +++++ apps/browser/src/_locales/fil/messages.json | 24 +++++ apps/browser/src/_locales/fr/messages.json | 26 ++++- apps/browser/src/_locales/gl/messages.json | 24 +++++ apps/browser/src/_locales/he/messages.json | 24 +++++ apps/browser/src/_locales/hi/messages.json | 24 +++++ apps/browser/src/_locales/hr/messages.json | 24 +++++ apps/browser/src/_locales/hu/messages.json | 42 +++++++-- apps/browser/src/_locales/id/messages.json | 24 +++++ apps/browser/src/_locales/it/messages.json | 24 +++++ apps/browser/src/_locales/ja/messages.json | 24 +++++ apps/browser/src/_locales/ka/messages.json | 24 +++++ apps/browser/src/_locales/km/messages.json | 24 +++++ apps/browser/src/_locales/kn/messages.json | 24 +++++ apps/browser/src/_locales/ko/messages.json | 24 +++++ apps/browser/src/_locales/lt/messages.json | 24 +++++ apps/browser/src/_locales/lv/messages.json | 24 +++++ apps/browser/src/_locales/ml/messages.json | 24 +++++ apps/browser/src/_locales/mr/messages.json | 24 +++++ apps/browser/src/_locales/my/messages.json | 24 +++++ apps/browser/src/_locales/nb/messages.json | 24 +++++ apps/browser/src/_locales/ne/messages.json | 24 +++++ apps/browser/src/_locales/nl/messages.json | 26 ++++- apps/browser/src/_locales/nn/messages.json | 24 +++++ apps/browser/src/_locales/or/messages.json | 24 +++++ apps/browser/src/_locales/pl/messages.json | 24 +++++ apps/browser/src/_locales/pt_BR/messages.json | 24 +++++ apps/browser/src/_locales/pt_PT/messages.json | 24 +++++ apps/browser/src/_locales/ro/messages.json | 24 +++++ apps/browser/src/_locales/ru/messages.json | 24 +++++ apps/browser/src/_locales/si/messages.json | 24 +++++ apps/browser/src/_locales/sk/messages.json | 24 +++++ apps/browser/src/_locales/sl/messages.json | 24 +++++ apps/browser/src/_locales/sr/messages.json | 26 ++++- apps/browser/src/_locales/sv/messages.json | 24 +++++ apps/browser/src/_locales/te/messages.json | 24 +++++ apps/browser/src/_locales/th/messages.json | 24 +++++ apps/browser/src/_locales/tr/messages.json | 26 ++++- apps/browser/src/_locales/uk/messages.json | 32 ++++++- apps/browser/src/_locales/vi/messages.json | 24 +++++ apps/browser/src/_locales/zh_CN/messages.json | 30 +++++- apps/browser/src/_locales/zh_TW/messages.json | 24 +++++ apps/browser/store/locales/az/copy.resx | 2 +- 61 files changed, 1519 insertions(+), 79 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index ba917a9e681..c3ba5c4a950 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "التعبئة التلقائية" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "إنشاء كلمة مرور (تم النسخ)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "لا توجد تسجيلات دخول مطابقة." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "افتح خزنتك" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index b784592a3b0..483dc2c14f1 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Avto-doldurma" }, + "autoFillLogin": { + "message": "Giriş avto-doldurma" + }, + "autoFillCard": { + "message": "Kart avto-doldurma" + }, + "autoFillIdentity": { + "message": "Kimlik avto-doldurma" + }, "generatePasswordCopied": { "message": "Parol yarat (kopyalandı)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Uyğun gələn hesab yoxdur." }, + "noCards": { + "message": "Kart yoxdur" + }, + "noIdentities": { + "message": "Kimlik yoxdur" + }, + "addLoginMenu": { + "message": "Giriş əlavə et" + }, + "addCardMenu": { + "message": "Kart əlavə et" + }, + "addIdentityMenu": { + "message": "Kimlik əlavə et" + }, "unlockVaultMenu": { "message": "Anbarın kilidini açın" }, @@ -671,7 +695,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { - "message": "Anbarı ixrac et" + "message": "Anbarı xaricə köçür" }, "fileFormat": { "message": "Fayl formatı" @@ -681,19 +705,19 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Anbarın ixracını təsdiqləyin" + "message": "Anbarın xaricə köçürülməsini təsdiqləyin" }, "exportWarningDesc": { - "message": "Bu ixrac faylındakı anbar verilənləriniz şifrələnməmiş formatdadır. İxrac edilən faylı, güvənli olmayan kanallar üzərində saxlamamalı və ya göndərməməlisiniz (e-poçt kimi). Bu faylı işiniz bitdikdən sonra dərhal silin." + "message": "Xaricə köçürdüyünüz bu fayldakı datanız şifrələnməmiş formatdadır. Bu faylı güvənli olmayan kanallar (e-poçt kimi) üzərində saxlamamalı və ya göndərməməlisiniz. İşiniz bitdikdən sonra faylı dərhal silin." }, "encExportKeyWarningDesc": { - "message": "Bu ixrac faylı, hesabınızın şifrələmə açarını istifadə edərək verilənlərinizi şifrələyir. Hesabınızın şifrələmə açarını döndərsəniz, bu ixrac faylının şifrəsini aça bilməyəcəyiniz üçün yenidən ixrac etməli olacaqsınız." + "message": "Xaricə köçürdüyünüz bu fayldakı data, hesabınızın şifrələmə açarı istifadə edilərək şifrələnir. Hesabınızın şifrələmə açarını dəyişdirsəniz, bu faylın şifrəsini aça bilməyəcəksiniz və onu yenidən xaricə köçürməli olacaqsınız." }, "encExportAccountWarningDesc": { "message": "Hesab şifrələmə açarları, hər Bitwarden istifadəçi hesabı üçün unikaldır, buna görə də şifrələnmiş bir ixracı, fərqli bir hesaba idxal edə bilməzsiniz." }, "exportMasterPassword": { - "message": "Anbar verilənlərinizi ixrac etmək üçün ana parolunuzu daxil edin." + "message": "Anbar datanızı xaricə köçürmək üçün ana parolunuzu daxil edin." }, "shared": { "message": "Paylaşılan" @@ -799,7 +823,7 @@ "message": "YubiKey və Duo kimi mülkiyyətçi iki addımlı giriş seçimləri." }, "ppremiumSignUpReports": { - "message": "Anbarınızın təhlükəsiyini təmin etmək üçün parol gigiyenası, hesab sağlamlığı və verilənlərin pozulması hesabatları." + "message": "Anbarınızın təhlükəsizliyini təmin etmək üçün parol gigiyenası, hesab sağlamlığı və data pozuntusu hesabatları." }, "ppremiumSignUpTotp": { "message": "Anbarınızdakı hesablar üçün TOTP təsdiqləmə kodu (2FA) yaradıcısı." @@ -2408,18 +2432,18 @@ "description": "Toggling an expand/collapse state." }, "aliasDomain": { - "message": "Alias domain" + "message": "Domen ləqəbi" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be auto-filled on page load. Auto-fill on page load turned off.", + "message": "\"Ana parolu təkrar soruş\" özəlliyi olan elementlər səhifə yüklənəndə avto-doldurulmur. \"Səhifə yüklənəndə avto-doldurma\" özəlliyi söndürülüb.", "description": "Toast message for describing that master password re-prompt cannot be auto-filled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Auto-fill on page load set to use default setting.", + "message": "\"Səhifə yüklənəndə avto-doldurma\" özəlliyi ilkin tənzimləməni istifadə etmək üzrə tənzimləndi.", "description": "Toast message for informing the user that auto-fill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "Bu sahəyə düzəliş etmək üçün \"Ana parolu təkrar soruş\"u söndürün", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index e941d525936..d0c1bac2bb7 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Аўтазапаўненне" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Генерыраваць пароль (з капіяваннем)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Няма адпаведных лагінаў." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Разблакіраваць сховішча" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 08451607d2b..c96fadbd422 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -14,7 +14,7 @@ "message": "Впишете се или създайте нов абонамент, за да достъпите защитен трезор." }, "createAccount": { - "message": "Създаване на абонамент" + "message": "Създаване на акаунт" }, "login": { "message": "Вписване" @@ -91,6 +91,15 @@ "autoFill": { "message": "Автоматично дописване" }, + "autoFillLogin": { + "message": "Авт. попълване на данни за вход" + }, + "autoFillCard": { + "message": "Самопопълваща се карта" + }, + "autoFillIdentity": { + "message": "Самопопълваща се самоличност" + }, "generatePasswordCopied": { "message": "Генериране на парола (копирана)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Няма съвпадащи записи." }, + "noCards": { + "message": "Няма карти" + }, + "noIdentities": { + "message": "Няма самоличности" + }, + "addLoginMenu": { + "message": "Добавяне на запис за вход" + }, + "addCardMenu": { + "message": "Добавяне на карта" + }, + "addIdentityMenu": { + "message": "Добавяне на самоличност" + }, "unlockVaultMenu": { "message": "Отключете трезора си" }, @@ -467,7 +491,7 @@ "message": "Грешен код за потвърждаване" }, "valueCopied": { - "message": "$VALUE$ — копирано", + "message": "Копирано е $VALUE$", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -537,7 +561,7 @@ "message": "Копирана парола" }, "uri": { - "message": "Адрес" + "message": "Унифициран идентификатор на ресурс" }, "uriPosition": { "message": "Адрес $POSITION$", @@ -550,7 +574,7 @@ } }, "newUri": { - "message": "Нов адрес" + "message": "Нов унифициран идентификатор на ресурс" }, "addedItem": { "message": "Елементът е добавен" @@ -631,7 +655,7 @@ "message": "Да се обнови ли паролата в Bitwarden?" }, "notificationChangeSave": { - "message": "Да, нека се обнови сега" + "message": "Осъвременяване" }, "notificationUnlockDesc": { "message": "Отключете трезора си в Битуорден, за да завършите заявката за автоматично попълване." @@ -684,7 +708,7 @@ "message": "Потвърждаване на изнасянето на трезора" }, "exportWarningDesc": { - "message": "Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като е-поща. Изтрийте файла незабавно след като свършите работата си с него." + "message": "Този износ съдържа данни на трезора ви в некриптиран формат. Не трябва да съхранявате или изпращате износния файл през незащитени канали (като имейл). Изтрийте файла моментално след като свършите работата си с него." }, "encExportKeyWarningDesc": { "message": "При изнасяне данните се шифрират с ключа ви. Ако го смените, ще трябва наново да ги изнесете, защото няма да може да дешифрирате настоящия файл." @@ -699,10 +723,10 @@ "message": "Споделено" }, "learnOrg": { - "message": "Разберете повече за организациите" + "message": "Научете за организациите" }, "learnOrgConfirmation": { - "message": "Битуорден позволява да споделяте части от трезора си чрез използването на организация. Искате ли да научите повече от сайта bitwarden.com?" + "message": "Битуорден позволява да споделяте елементи от трезора си а други, използвайки организация. Бихте ли посетили сайта bitwarden.com, за да научите повече?" }, "moveToOrganization": { "message": "Преместване в организация" @@ -781,7 +805,7 @@ "message": "Управление на абонамента" }, "premiumManageAlert": { - "message": "Можете да управлявате абонамента си през сайта bitwarden.com. Искате ли да го посетите сега?" + "message": "Може да управлявате членството си в мрежата на трезора в bitwarden.com. Искате ли да посетите уебсайта сега?" }, "premiumRefresh": { "message": "Опресняване на абонамента" @@ -901,7 +925,7 @@ "message": "Регистрацията е защитена с двустепенно удостоверяване, но никой от настроените доставчици на удостоверяване не се поддържа от този браузър." }, "noTwoStepProviders2": { - "message": "Пробвайте с поддържан уеб браузър (като Chrome или Firefox) и други доставчици на удостоверяване, които се поддържат от браузърите (като специални програми за удостоверяване)." + "message": "Употребявайте поддържан браузър (като Chrome, Firefox) и/или добавете други доставчици на удостоверяване, които се поддържат по-добре от браузърите (като специални програми за удостоверяване)." }, "twoStepOptions": { "message": "Настройки на двустепенното удостоверяване" @@ -920,10 +944,10 @@ "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { - "message": "Устройство YubiKey OTP" + "message": "Ключ за сигурност YubiKey OTP" }, "yubiKeyDesc": { - "message": "Използвайте устройство на YubiKey, за да влезете в абонамента си. Поддържат се моделите YubiKey 4, 4 Nano, 4C и NEO." + "message": "Използвайте ключа за сигурност YubiKey, за да влезете в акаунта си. Работи с устройствата YubiKey 4, 4 Nano, 4C и NEO." }, "duoDesc": { "message": "Удостоверяване чрез Duo Security, с ползване на приложението Duo Mobile, SMS, телефонен разговор или устройство U2F.", @@ -2096,7 +2120,7 @@ "message": "собствен хостинг" }, "thirdParty": { - "message": "Third-party" + "message": "Трета страна" }, "thirdPartyServerMessage": { "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index edee2852116..f8c5fb8c85b 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "স্বতঃপূরণ" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "পাসওয়ার্ড তৈরি করুন (অনুলিপিকৃত)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "কোনও মিলত লগইন নেই।" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index e984a88b7bc..d5d1bbb16a6 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 7839cceebcb..af85e586c80 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Emplenament automàtic" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Genera contrasenya (copiada)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No hi ha inicis de sessió coincidents." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "1. Desbloquegeu la caixa forta." }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 71446db9f2c..e9b3bf997cd 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automatické vyplňování" }, + "autoFillLogin": { + "message": "Automaticky vyplnit přihlášení" + }, + "autoFillCard": { + "message": "Automaticky vyplnit kartu" + }, + "autoFillIdentity": { + "message": "Automaticky vyplnit identitu" + }, "generatePasswordCopied": { "message": "Vygenerovat heslo a zkopírovat do schránky" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Žádné odpovídající přihlašovací údaje" }, + "noCards": { + "message": "Žádné karty" + }, + "noIdentities": { + "message": "Žádné identity" + }, + "addLoginMenu": { + "message": "Přidat přihlašovací údaje" + }, + "addCardMenu": { + "message": "Přidat kartu" + }, + "addIdentityMenu": { + "message": "Přidat identitu" + }, "unlockVaultMenu": { "message": "Odemknout Váš trezor" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 7292370f533..ff3ccc684f7 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Llenwi'n awtomatig" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Cynhyrchu cyfrinair (wedi'i gopïo)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Datgloi'ch cell" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 1bc0ed79e5a..5dd437827f3 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-udfyld" }, + "autoFillLogin": { + "message": "Autoudfyld login" + }, + "autoFillCard": { + "message": "Autoudfyld kort" + }, + "autoFillIdentity": { + "message": "Autoudfyld identitet" + }, "generatePasswordCopied": { "message": "Generér adgangskode (kopieret)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Ingen matchende logins" }, + "noCards": { + "message": "Ingen kort" + }, + "noIdentities": { + "message": "Ingen identiteter" + }, + "addLoginMenu": { + "message": "Tilføj login" + }, + "addCardMenu": { + "message": "Tilføj kort" + }, + "addIdentityMenu": { + "message": "Tilføj identitet" + }, "unlockVaultMenu": { "message": "Lås din boks op" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 443d4294cd3..3edd462918f 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-Ausfüllen" }, + "autoFillLogin": { + "message": "Zugangsdaten automatisch ausfüllen" + }, + "autoFillCard": { + "message": "Karte automatisch ausfüllen" + }, + "autoFillIdentity": { + "message": "Identität automatisch ausfüllen" + }, "generatePasswordCopied": { "message": "Passwort generieren (kopiert)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Keine passenden Zugangsdaten" }, + "noCards": { + "message": "Keine Karten" + }, + "noIdentities": { + "message": "Keine Identitäten" + }, + "addLoginMenu": { + "message": "Zugangsdaten hinzufügen" + }, + "addCardMenu": { + "message": "Karte hinzufügen" + }, + "addIdentityMenu": { + "message": "Identität hinzufügen" + }, "unlockVaultMenu": { "message": "Entsperre deinen Tresor" }, diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index c0b349f8aff..2692a89b7db 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Αυτόματη συμπλήρωση" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Δημιουργία Κωδικού (αντιγράφηκε)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Δεν υπάρχουν αντιστοιχίσεις σύνδεσης." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Ξεκλειδώστε το vault σας" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index fbe9e4fc31e..4e1057956fa 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 0ae234fbece..32dd196f2ae 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 15f8b4fbefa..b81a05c9a4b 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Autorellenar" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generar contraseña (copiada)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Sin entradas coincidentes." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Desbloquea la caja fuerte" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index b799c3a63a3..4fe20767c0e 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automaatne täitmine" }, + "autoFillLogin": { + "message": "Täida konto andmed" + }, + "autoFillCard": { + "message": "Täida kaardi andmed" + }, + "autoFillIdentity": { + "message": "Täida identiteet" + }, "generatePasswordCopied": { "message": "Genereeri parool (kopeeritakse)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Sobivaid kontoandmeid ei leitud." }, + "noCards": { + "message": "Kaardid puuduvad" + }, + "noIdentities": { + "message": "Identiteedid puuduvad" + }, + "addLoginMenu": { + "message": "Lisa konto andmed" + }, + "addCardMenu": { + "message": "Lisa kaart" + }, + "addIdentityMenu": { + "message": "Lisa identiteet" + }, "unlockVaultMenu": { "message": "Lukusta hoidla lahti" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 6290304cda6..291a687f8b4 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-betetzea" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Sortu pasahitza (kopiatuta)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Bat datozen saio-hasierarik gabe" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Desblokeatu kutxa gotorra" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 250ca3631a5..bcfab0cdbc8 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "پر کردن خودکار" }, + "autoFillLogin": { + "message": "پر کردن خودکار ورود" + }, + "autoFillCard": { + "message": "پر کردن خودکار کارت" + }, + "autoFillIdentity": { + "message": "پر کردن خودکار هویت" + }, "generatePasswordCopied": { "message": "ساخت کلمه عبور (کپی شد)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "ورودی‌ها منتطبق نیست" }, + "noCards": { + "message": "کارتی وجود ندارد" + }, + "noIdentities": { + "message": "هویتی وجود ندارد" + }, + "addLoginMenu": { + "message": "افزودن ورود" + }, + "addCardMenu": { + "message": "افزودن کارت" + }, + "addIdentityMenu": { + "message": "افزودن هویت" + }, "unlockVaultMenu": { "message": "قفل گاوصندوق خود را باز کنید" }, @@ -637,7 +661,7 @@ "message": "برای پر کردن خودکار گاوصندوق Bitwarden خود را باز کنید." }, "notificationUnlock": { - "message": "بازگشایی" + "message": "باز کردن قفل" }, "enableContextMenuItem": { "message": "نمایش گزینه‌های منوی زمینه" @@ -772,7 +796,7 @@ "message": "ویژگی موجود نیست" }, "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "message": "انتقال کلید رمزگذاری مورد نیاز است. لطفاً از طریق گاوصندوق وب وارد شوید تا کلید رمزگذاری خود را به روز کنید." }, "premiumMembership": { "message": "عضویت پرمیوم" @@ -1606,10 +1630,10 @@ "message": "بیومتریک مرورگر در این دستگاه پشتیبانی نمی‌شود." }, "biometricsFailedTitle": { - "message": "زیست‌سنجی ناتمام ماند" + "message": "زیست‌سنجی ناموفق بود" }, "biometricsFailedDesc": { - "message": "زیست‌سنجی نمی تواند انجام شود، استفاده از کلمه عبور اصلی یا خروج را در نظر بگیرید. اگر این مشکل ادامه یافت لطفا با پشتیبانی Bitwarden تماس بگیرید." + "message": "زیست‌سنجی نمی‌تواند انجام شود، استفاده از کلمه عبور اصلی یا خروج را در نظر بگیرید. اگر این مشکل ادامه یافت لطفاً با پشتیبانی Bitwarden تماس بگیرید." }, "nativeMessaginPermissionErrorTitle": { "message": "مجوز ارائه نشده است" @@ -1992,7 +2016,7 @@ "message": "برون ریزی گاو‌صندوق شخصی" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "فقط موارد شخصی گاوصندوق مرتبط با $EMAIL$ برون ریزی خواهند شد. موارد گاوصندوق سازمان شامل نخواهد شد. فقط اطلاعات مورد گاوصندوق برون ریزی خواهد شد و شامل تاریخچه کلمه عبور مرتبط یا پیوست نمی‌شود.", "placeholders": { "email": { "content": "$1", @@ -2234,28 +2258,28 @@ } }, "loggingInOn": { - "message": "ورود به عنوان" + "message": "ورود با" }, "opensInANewWindow": { "message": "در پنجره جدید باز می‌شود" }, "deviceApprovalRequired": { - "message": "تایید دستگاه لازم است. یک روش تایید برگزینید:" + "message": "تأیید دستگاه لازم است. یک روش تأیید انتخاب کنید:" }, "rememberThisDevice": { "message": "این دستگاه را به خاطر بسپار" }, "uncheckIfPublicDevice": { - "message": "بردارید اگر از دستگاه عمومی استفاده میکنید" + "message": "اگر از دستگاه عمومی استفاده می‌کنید علامت را بردارید" }, "approveFromYourOtherDevice": { - "message": "تایید با دستگاه دیگرتان" + "message": "تأیید با دستگاه دیگرتان" }, "requestAdminApproval": { - "message": "درخواست تایید مدیر" + "message": "درخواست تأیید مدیر" }, "approveWithMasterPassword": { - "message": "تایید با کلمه عبور اصلی" + "message": "تأیید با کلمه عبور اصلی" }, "ssoIdentifierRequired": { "message": "شناسه سازمان SSO مورد نیاز است." @@ -2283,37 +2307,37 @@ "message": "حساب کاربری با موفقیت ایجاد شد!" }, "adminApprovalRequested": { - "message": "تایید مدیر در خواست شد" + "message": "تأیید مدیر درخواست شد" }, "adminApprovalRequestSentToAdmins": { "message": "درخواست شما به مدیرتان فرستاده شد." }, "youWillBeNotifiedOnceApproved": { - "message": "به محض تایید مطلع خواهید شد." + "message": "به محض تأیید مطلع خواهید شد." }, "troubleLoggingIn": { "message": "در ورود مشکلی دارید؟" }, "loginApproved": { - "message": "ورود تایید شد" + "message": "ورود تأیید شد" }, "userEmailMissing": { - "message": "رایانامه کاربر کم است" + "message": "ایمیل کاربر وجود ندارد" }, "deviceTrusted": { "message": "دستگاه مورد اعتماد است" }, "inputRequired": { - "message": "ورودی مورد نیاز است." + "message": "ورودی ضروری است." }, "required": { - "message": "الزامی" + "message": "ضروری" }, "search": { "message": "جستجو" }, "inputMinLength": { - "message": "ورودی باید حداقل $COUNT$ نشانه داشته باشد.", + "message": "ورودی باید حداقل $COUNT$ کاراکتر داشته باشد.", "placeholders": { "count": { "content": "$1", @@ -2322,7 +2346,7 @@ } }, "inputMaxLength": { - "message": "اندازه ورودی نباید بیش از $COUNT$ نشانه باشد.", + "message": "طول ورودی نباید بیش از $COUNT$ کاراکتر باشد.", "placeholders": { "count": { "content": "$1", @@ -2331,7 +2355,7 @@ } }, "inputForbiddenCharacters": { - "message": "نشانه های زیر مجاز نیستند: $CHARACTERS$", + "message": "کاراکترهای زیر مجاز نیستند: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2340,7 +2364,7 @@ } }, "inputMinValue": { - "message": "مقدار ورودی باید دست کم $MIN$ باشد.", + "message": "مقدار ورودی باید حداقل $MIN$ باشد.", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2373,7 @@ } }, "inputMaxValue": { - "message": "مقدار ورودی نباید بیش از $MAX$ باشد.", + "message": "مقدار ورودی نباید از $MAX$ تجاوز کند.", "placeholders": { "max": { "content": "$1", @@ -2358,17 +2382,17 @@ } }, "multipleInputEmails": { - "message": "یک یا چند رایانامه نامعتبر است" + "message": "یک یا چند ایمیل نامعتبر است" }, "inputTrimValidator": { - "message": "ورودی نباید فقط فاصله باشد.", + "message": "ورودی نباید فقط حاوی فضای خالی باشد.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "ورودی یک نشانی رایانامه نیست." + "message": "ورودی یک نشانی ایمیل نیست." }, "fieldsNeedAttention": { - "message": "بخش (های) $COUNT$ در بالا نیازمند توجه شما است.", + "message": "فیلد $COUNT$ در بالا به توجه شما نیاز دارد.", "placeholders": { "count": { "content": "$1", @@ -2380,16 +2404,16 @@ "message": "-- انتخاب --" }, "multiSelectPlaceholder": { - "message": "-- برای گزینش چیزی بنویسید --" + "message": "-- برای فیلتر تایپ کنید --" }, "multiSelectLoading": { "message": "در حال بازیابی گزینه‌ها..." }, "multiSelectNotFound": { - "message": "موردی پیدا نشد" + "message": "موردی یافت نشد" }, "multiSelectClearAll": { - "message": "پاک کردن همه" + "message": "پاک‌کردن همه" }, "plusNMore": { "message": "+ $QUANTITY$ بیشتر", @@ -2401,25 +2425,25 @@ } }, "submenu": { - "message": "زیرفهرست" + "message": "زیرمنو" }, "toggleCollapse": { - "message": "باز و بسته کردن", + "message": "دکمه بستن", "description": "Toggling an expand/collapse state." }, "aliasDomain": { - "message": "Alias domain" + "message": "دامنه مستعار" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be auto-filled on page load. Auto-fill on page load turned off.", + "message": "موارد با درخواست مجدد کلمه عبور اصلی را نمی‌توان در بارگذاری صفحه به‌صورت خودکار پر کرد. پر کردن خودکار در بارگیری صفحه خاموش شد.", "description": "Toast message for describing that master password re-prompt cannot be auto-filled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Auto-fill on page load set to use default setting.", + "message": "پر کردن خودکار در بارگیری صفحه برای استفاده از تنظیمات پیش‌فرض تنظیم شده است.", "description": "Toast message for informing the user that auto-fill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "برای ویرایش این فیلد، درخواست مجدد کلمه عبور اصلی را خاموش کنید", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 3a829d4783d..daaa9a89258 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automaattinen täyttö" }, + "autoFillLogin": { + "message": "Täytä kirjautumistieto automaattisesti" + }, + "autoFillCard": { + "message": "Täytä kortti automaattisesti" + }, + "autoFillIdentity": { + "message": "Täytä identiteetti automaattisesti" + }, "generatePasswordCopied": { "message": "Luo salasana (leikepöydälle)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Ei tunnistettuja kirjautumistietoja." }, + "noCards": { + "message": "Kortteja ei ole" + }, + "noIdentities": { + "message": "Identiteettejä ei ole" + }, + "addLoginMenu": { + "message": "Lisää kirjautumistieto" + }, + "addCardMenu": { + "message": "Lisää kortti" + }, + "addIdentityMenu": { + "message": "Lisää identiteetti" + }, "unlockVaultMenu": { "message": "Avaa holvisi" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index a8673d001ae..464cf888899 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill sa Filipino ay Awtomatikong Pagpuno" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Maglagay ng Password" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Walang tumutugmang mga login" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Buksan ang iyong kahadeyero" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index ba4d6a6dd86..b0827b39006 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Saisie automatique" }, + "autoFillLogin": { + "message": "Saisie automatique de l'identifiant" + }, + "autoFillCard": { + "message": "Saisie automatique de la carte" + }, + "autoFillIdentity": { + "message": "Saisie automatique de l'identité" + }, "generatePasswordCopied": { "message": "Générer un mot de passe (copié)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Aucun identifiant correspondant." }, + "noCards": { + "message": "Aucune carte" + }, + "noIdentities": { + "message": "Aucune identité" + }, + "addLoginMenu": { + "message": "Ajouter un identifiant" + }, + "addCardMenu": { + "message": "Ajouter une carte" + }, + "addIdentityMenu": { + "message": "Ajouter une identité" + }, "unlockVaultMenu": { "message": "Déverrouillez votre coffre" }, @@ -634,7 +658,7 @@ "message": "Mettre à jour" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "Déverrouillez votre coffre Bitwarden pour terminer la demande de saisie automatique." }, "notificationUnlock": { "message": "Déverrouiller" diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 68f1ebc1218..19985cd5ec8 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "השלמה אוטומטית" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "צור סיסמה (העתק)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "לא נמצאו פרטי כניסה תואמים." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "שחרור הכספת שלך" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 2b99c964209..4b686192902 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "स्वत:भरण" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate Password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "कोई मेल-मिला लॉगिन नहीं |" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "आपकी तिजोरी का ताला खोलें" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index b88c097804f..b334d22332f 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-ispuna" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generiraj lozinku (i kopiraj)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Nema podudarajućih prijava" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Otključaj svoj trezor" }, diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index efe6d260355..abf5a12e5e5 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automatikus kitöltés" }, + "autoFillLogin": { + "message": "Automatikus kitöltés bejelentkezés" + }, + "autoFillCard": { + "message": "Automatikus kitöltés kártya" + }, + "autoFillIdentity": { + "message": "Automatikus kitöltés személyazonosság" + }, "generatePasswordCopied": { "message": "Jelszó generálás (másolt)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Nincsenek egyező bejelentkezések." }, + "noCards": { + "message": "Nincsenek kártyák" + }, + "noIdentities": { + "message": "Nincsenek személyazonosságok" + }, + "addLoginMenu": { + "message": "Bejelentkezés hozzáadása" + }, + "addCardMenu": { + "message": "Kártya hozzáadása" + }, + "addIdentityMenu": { + "message": "Személyazonossság hozzáadása" + }, "unlockVaultMenu": { "message": "Széf kinyitása" }, @@ -143,7 +167,7 @@ "message": "A folytatáshoz meg kell erősíteni a személyazonosságot." }, "account": { - "message": "Felhasználó" + "message": "Fiók" }, "changeMasterPassword": { "message": "Mesterjelszó módosítása" @@ -513,7 +537,7 @@ "message": "A kétlépcsős bejelentkezés biztonságosabbá teszi a fiókot azáltal, hogy ellenőrizni kell a bejelentkezést egy másik olyan eszközzel mint például biztonsági kulcs, hitelesítő alkalmazás, SMS, telefon hívás vagy email. A kétlépcsős bejelentkezést a bitwarden.com webes széfben lehet engedélyezni. Felkeressük a webhelyet most?" }, "editedFolder": { - "message": "A mappa módosításra került." + "message": "A mappa mentésre került." }, "deleteFolderConfirmation": { "message": "Biztos, hogy törölni akarod ezt a mappát?" @@ -562,7 +586,7 @@ "message": "Biztosan törlésre kerüljön ezt az elem?" }, "deletedItem": { - "message": "Az elem törlésre került." + "message": "Az elem a lomtárba került." }, "overwritePassword": { "message": "Jelszó felülírása" @@ -769,7 +793,7 @@ "message": "A naximális fájlméret 500 MB." }, "featureUnavailable": { - "message": "Ez a funkció nem érhető el." + "message": "A funkció nem érhető el." }, "encryptionKeyMigrationRequired": { "message": "Titkosítási kulcs migráció szükséges. Jelentkezzünk be a webes széfen keresztül a titkosítási kulcs frissítéséhez." @@ -787,7 +811,7 @@ "message": "Tagság frissítése" }, "premiumNotCurrentMember": { - "message": "Jelenleg nincs prémium tagság." + "message": "Jelenleg nem vagyunk prémium tag." }, "premiumSignUpAndGet": { "message": "Regisztráció a prémium tagságra az alábbi funkciókért:" @@ -796,7 +820,7 @@ "message": "1 GB titkosított tárhely a fájlmellékleteknek." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Saját kétlépcsős bejelentkezési lehetőségek mint a YubiKey és a Duo." }, "ppremiumSignUpReports": { "message": "Jelszó higiénia, fiók biztonság és adatszivárgási jelentések a széf biztonsága érdekében." @@ -817,7 +841,7 @@ "message": "A prémium tagság megvásárolható a bitwarden.com webes széfben. Szeretnénk felkeresni a webhelyet most?" }, "premiumCurrentMember": { - "message": "Jelenleg a prémium tagság érvényben van." + "message": "Prémium tag vagyunk!" }, "premiumCurrentMemberThanks": { "message": "Köszönjük a Bitwarden támogatását." @@ -994,7 +1018,7 @@ "message": "Alapértelmezett beállítások bejelentkezési elemekhez" }, "defaultAutoFillOnPageLoadDesc": { - "message": "Az Automatikus kitöltés engedélyezése az oldalbetöltéskor engedélyezheti vagy letilthatja a funkciót az egyes bejelentkezési elemeknél. Ez az alapértelmezett beállítás a bejelentkezési elemeknéll, amelyek nincsenek külön konfigurálva." + "message": "Az egyes bejelentkezési elemeknél kikapcsolhatjuk oldalbetöltéskor az automatikus kitöltést az elem Szerkesztés nézetében." }, "itemAutoFillOnPageLoad": { "message": "Automatikus kitöltés oldal betöltésnél (Ha engedélyezett az opcióknál)" @@ -1905,7 +1929,7 @@ "message": "Perc" }, "vaultTimeoutPolicyInEffect": { - "message": "A szervezeti házirendek hatással vannak a széf időkorlátjára. A széf időkorlátja legfeljebb $HOURS$ óra és $MINUTES$ perc lehet.", + "message": "A szervezeti szabályzata $HOURS$ órára és $MINUTES$ percre állította be a maximálisan megengedett széf időtúllépést.", "placeholders": { "hours": { "content": "$1", diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index f3fdd1a7c27..7b19a6838c0 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Isi otomatis" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Membuat Kata Sandi (tersalin)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Tidak ada info masuk yang cocok." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Buka brankas Anda" }, diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 691e2484cba..9bca2da6062 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Riempimento automatico" }, + "autoFillLogin": { + "message": "Riempi automaticamente login" + }, + "autoFillCard": { + "message": "Riempi automaticamente carta" + }, + "autoFillIdentity": { + "message": "Riempi automaticamente identità" + }, "generatePasswordCopied": { "message": "Genera password e copiala" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Nessun login corrispondente" }, + "noCards": { + "message": "Nessuna carta" + }, + "noIdentities": { + "message": "Nessuna identità" + }, + "addLoginMenu": { + "message": "Aggiungi login" + }, + "addCardMenu": { + "message": "Aggiungi carta" + }, + "addIdentityMenu": { + "message": "Aggiungi identità" + }, "unlockVaultMenu": { "message": "Sblocca la tua cassaforte" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index c3762ae4ef8..d2528a45489 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "自動入力" }, + "autoFillLogin": { + "message": "自動入力ログイン" + }, + "autoFillCard": { + "message": "自動入力カード" + }, + "autoFillIdentity": { + "message": "自動入力 ID" + }, "generatePasswordCopied": { "message": "パスワードを生成 (コピー)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "一致するログインがありません。" }, + "noCards": { + "message": "カードなし" + }, + "noIdentities": { + "message": "ID なし" + }, + "addLoginMenu": { + "message": "ログイン情報を追加" + }, + "addCardMenu": { + "message": "カードを追加" + }, + "addIdentityMenu": { + "message": "ID を追加" + }, "unlockVaultMenu": { "message": "保管庫のロックを解除" }, diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 50a25ad0dd9..2d2b5cb8a5e 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "თვითშევსება" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 06f9461e5fd..95c9350aff8 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "ಸ್ವಯಂ ಭರ್ತಿ" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ (ನಕಲಿಸಲಾಗಿದೆ)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "ಹೊಂದಾಣಿಕೆಯ ಲಾಗಿನ್‌ಗಳು ಇಲ್ಲ." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 5921e579e24..e7aba95f493 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "자동 완성" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "비밀번호 생성 및 클립보드에 복사" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "사용할 수 있는 로그인이 없습니다." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "보관함 잠금 해제" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 425d0c98233..885b3cbec8f 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automatinis užpildymas" }, + "autoFillLogin": { + "message": "Automatinio užpildymo prisijungimas" + }, + "autoFillCard": { + "message": "Automatinio užpildymo kortelė" + }, + "autoFillIdentity": { + "message": "Automatinio užpildymo tapatybė" + }, "generatePasswordCopied": { "message": "Kurti slaptažodį (paruoštas įterpti)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Nėra atitinkančių prisijungimų." }, + "noCards": { + "message": "Nėra kortelių" + }, + "noIdentities": { + "message": "Nėra tapatybių" + }, + "addLoginMenu": { + "message": "Pridėti prisijungimą" + }, + "addCardMenu": { + "message": "Pridėti kortelę" + }, + "addIdentityMenu": { + "message": "Pridėti tapatybę" + }, "unlockVaultMenu": { "message": "Atrakinti saugyklą" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 624904d7213..2abb0ac4de6 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automātiskā aizpildīšana" }, + "autoFillLogin": { + "message": "Automātiski aizpildīt pieteikšanos" + }, + "autoFillCard": { + "message": "Automātiski aizpildīt karti" + }, + "autoFillIdentity": { + "message": "Automātiski aizpildīt identitāti" + }, "generatePasswordCopied": { "message": "Izveidot paroli (tiks ievietota starpliktuvē)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Nav atbilstošu pieteikšanās vienumu" }, + "noCards": { + "message": "Nav karšu" + }, + "noIdentities": { + "message": "Nav identitāšu" + }, + "addLoginMenu": { + "message": "Pievienot pieteikšanās vienumu" + }, + "addCardMenu": { + "message": "Pievienot karti" + }, + "addIdentityMenu": { + "message": "Pievienot identitāti" + }, "unlockVaultMenu": { "message": "Atslēgt glabātavu" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 58d5b04fc8b..258ad3fd966 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "ഓട്ടോഫിൽ" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "പാസ്‌വേഡ് സൃഷ്ടിക്കുക (പകർത്തുക )" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "പൊരുത്തപ്പെടുന്ന ലോഗിനുകളൊന്നുമില്ല." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 8f8f58775f7..131c062d544 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "स्वयंभरण" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "तिजोरी उघडा" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 4dadbbc8a7b..43a19478bb0 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-utfylling" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generer et passord (kopiert)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Ingen samsvarende innlogginger." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Lås opp hvelvet ditt" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 5915dad872a..630510f6723 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-invullen" }, + "autoFillLogin": { + "message": "Login automatisch invullen" + }, + "autoFillCard": { + "message": "Kaart automatisch invullen" + }, + "autoFillIdentity": { + "message": "Identiteit automatisch invullen" + }, "generatePasswordCopied": { "message": "Wachtwoord genereren (op klembord)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Geen overeenkomstige logins." }, + "noCards": { + "message": "Geen kaarten" + }, + "noIdentities": { + "message": "Geen identiteiten" + }, + "addLoginMenu": { + "message": "Login toevoegen" + }, + "addCardMenu": { + "message": "Kaart toevoegen" + }, + "addIdentityMenu": { + "message": "Identiteit toevoegen" + }, "unlockVaultMenu": { "message": "Ontgrendel je kluis" }, @@ -772,7 +796,7 @@ "message": "Functionaliteit niet beschikbaar" }, "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "message": "Migratie van de encryptiesleutel vereist. Login via de website om je sleutel te bij te werken." }, "premiumMembership": { "message": "Premium-abonnement" diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 2f28c87d801..5b16c13ef14 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Autouzupełnianie" }, + "autoFillLogin": { + "message": "Autouzupełnianie logowania" + }, + "autoFillCard": { + "message": "Autouzupełnianie karty" + }, + "autoFillIdentity": { + "message": "Autouzupełnianie tożsamości" + }, "generatePasswordCopied": { "message": "Wygeneruj hasło (do schowka)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Brak pasujących danych logowania" }, + "noCards": { + "message": "Brak kart" + }, + "noIdentities": { + "message": "Brak tożsamości" + }, + "addLoginMenu": { + "message": "Dodaj dane logowania" + }, + "addCardMenu": { + "message": "Dodaj kartę" + }, + "addIdentityMenu": { + "message": "Dodaj tożsamość" + }, "unlockVaultMenu": { "message": "Odblokuj sejf" }, diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index ed62f893a19..7578ae170ca 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Autopreencher" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Gerar Senha (copiada)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Sem credenciais correspondentes." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Desbloqueie seu cofre" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 735edf60afe..1495b64e453 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Preenchimento automático" }, + "autoFillLogin": { + "message": "Preenchimento automático da credencial" + }, + "autoFillCard": { + "message": "Preenchimento automático do cartão" + }, + "autoFillIdentity": { + "message": "Preenchimento automático da identidade" + }, "generatePasswordCopied": { "message": "Gerar palavra-passe (copiada)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Sem credenciais correspondentes" }, + "noCards": { + "message": "Sem cartões" + }, + "noIdentities": { + "message": "Sem identidades" + }, + "addLoginMenu": { + "message": "Adicionar credencial" + }, + "addCardMenu": { + "message": "Adicionar cartão" + }, + "addIdentityMenu": { + "message": "Adicionar identidade" + }, "unlockVaultMenu": { "message": "Desbloquear o cofre" }, diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index f525f87b6c2..085951a93f1 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-completare" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generare parolă (s-a copiat)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Nu există potrivire de autentificări" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Deblocați-vă seiful" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 40452e56dce..40ecd382bd5 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Автозаполнение" }, + "autoFillLogin": { + "message": "Автозаполнение логина" + }, + "autoFillCard": { + "message": "Автозаполнение карты" + }, + "autoFillIdentity": { + "message": "Автозаполнение личности" + }, "generatePasswordCopied": { "message": "Сгенерировать пароль (с копированием)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Нет подходящих логинов." }, + "noCards": { + "message": "Нет карт" + }, + "noIdentities": { + "message": "Нет личностей" + }, + "addLoginMenu": { + "message": "Добавить логин" + }, + "addCardMenu": { + "message": "Добавить карту" + }, + "addIdentityMenu": { + "message": "Добавить личность" + }, "unlockVaultMenu": { "message": "Разблокировать хранилище" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 04f2a619a82..8ea364cb6d1 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "ස්වයං-පිරවීම" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "මුරපදය ජනනය (පිටපත්)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "ගැලපෙන පිවිසුම් නොමැත." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 684c677c45b..d04f2ccf05a 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Automatické vypĺňanie" }, + "autoFillLogin": { + "message": "Automatické vyplnenie prihlasovacích údajov" + }, + "autoFillCard": { + "message": "Automatické vyplnenie karty" + }, + "autoFillIdentity": { + "message": "Automatické vyplnenie identity" + }, "generatePasswordCopied": { "message": "Vygenerovať heslo (skopírované)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Žiadne zodpovedajúce prihlasovacie údaje." }, + "noCards": { + "message": "Žiadne karty" + }, + "noIdentities": { + "message": "Žiadne identity" + }, + "addLoginMenu": { + "message": "Pridať prihlasovacie údaje" + }, + "addCardMenu": { + "message": "Pridať kartu" + }, + "addIdentityMenu": { + "message": "Pridať identitu" + }, "unlockVaultMenu": { "message": "Odomknúť trezor" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 8878c308045..5f859e9e5f3 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Samodejno izpolnjevanje" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generiraj geslo (kopirano)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Ni ustreznih prijav." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Odkleni svoj trezor" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 46e96c7d8df..d55a5bfe197 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Аутоматско допуњавање" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Генериши Лозинку (копирано)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Нема одговарајућих пријављивања." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Откључај свој сеф" }, @@ -2408,7 +2432,7 @@ "description": "Toggling an expand/collapse state." }, "aliasDomain": { - "message": "Alias domain" + "message": "Домен алијаса" }, "passwordRepromptDisabledAutofillOnPageLoad": { "message": "Ставке са упитом за поновно постављање главне лозинке не могу се ауто-попунити при учитавању странице. Ауто-попуњавање при учитавању странице је искључено.", diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 680a3aac0bd..ca0b8de6580 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Fyll i automatiskt" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Skapa lösenord (kopierad)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Inga matchande inloggningar" }, + "noCards": { + "message": "Inga kort" + }, + "noIdentities": { + "message": "Inga identiteter" + }, + "addLoginMenu": { + "message": "Lägg till inloggning" + }, + "addCardMenu": { + "message": "Lägg till kort" + }, + "addIdentityMenu": { + "message": "Lägg till identitet" + }, "unlockVaultMenu": { "message": "Lås upp ditt valv" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index f8ffeff747e..22330901579 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Auto-fill" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "No matching logins" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Unlock your vault" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 0db240ea797..20702a1de4a 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "กรอกข้อมูลอัตโนมัติ" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Generate Password (copied)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "ไม่พบข้อมูลล็อกอินที่ตรงกัน" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "ปลดล็อกกตู้นิรภัยของคุณ" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index e52a2f59f26..e9763386aa6 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Otomatik doldur" }, + "autoFillLogin": { + "message": "Hesabı otomatik doldur" + }, + "autoFillCard": { + "message": "Kartı otomatik doldur" + }, + "autoFillIdentity": { + "message": "Kimliği otomatik doldur" + }, "generatePasswordCopied": { "message": "Parola oluştur (ve kopyala)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Eşleşen hesap yok" }, + "noCards": { + "message": "Kart yok" + }, + "noIdentities": { + "message": "Kimlik yok" + }, + "addLoginMenu": { + "message": "Hesap ekle" + }, + "addCardMenu": { + "message": "Kart ekle" + }, + "addIdentityMenu": { + "message": "Kimlik ekle" + }, "unlockVaultMenu": { "message": "Kasanızın kilidini açın" }, @@ -1609,7 +1633,7 @@ "message": "Biyometri doğrulanamadı" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "Biyometri doğrulaması tamamlanamadı. Ana parolanızı kullanabilir veya çıkış yapabilirsiniz. Sorun devam ederse Bitwarden destek ekibiyle iletişime geçin." }, "nativeMessaginPermissionErrorTitle": { "message": "İzin verilmedi" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 6cb70bd76bd..666870146d4 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Автозаповнення" }, + "autoFillLogin": { + "message": "Автозаповнення входу" + }, + "autoFillCard": { + "message": "Автозаповнення картки" + }, + "autoFillIdentity": { + "message": "Автозаповнення особистих даних" + }, "generatePasswordCopied": { "message": "Генерувати пароль (з копіюванням)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Немає відповідних записів" }, + "noCards": { + "message": "Немає карток" + }, + "noIdentities": { + "message": "Немає особистих даних" + }, + "addLoginMenu": { + "message": "Додати запис входу" + }, + "addCardMenu": { + "message": "Додати картку" + }, + "addIdentityMenu": { + "message": "Додати особисті дані" + }, "unlockVaultMenu": { "message": "Розблокуйте сховище" }, @@ -2408,18 +2432,18 @@ "description": "Toggling an expand/collapse state." }, "aliasDomain": { - "message": "Alias domain" + "message": "Псевдонім домену" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be auto-filled on page load. Auto-fill on page load turned off.", + "message": "Записи з повторним запитом головного пароля не можна автоматично заповнювати під час завантаження сторінки. Автозаповнення на сторінці вимкнено.", "description": "Toast message for describing that master password re-prompt cannot be auto-filled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Auto-fill on page load set to use default setting.", + "message": "Автозаповнення на сторінці налаштовано з типовими параметрами.", "description": "Toast message for informing the user that auto-fill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "Вимкніть повторний запит головного пароля, щоб редагувати це поле", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index e562a0b5d5d..caedbb8b917 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "Tự động điền" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "Tạo mật khẩu (đã sao chép)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "Không có thông tin đăng nhập phù hợp." }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "Mở khoá kho lưu trữ của bạn" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index b1d0aac7e4a..5331208af82 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "自动填充" }, + "autoFillLogin": { + "message": "自动填充登录" + }, + "autoFillCard": { + "message": "自动填充支付卡" + }, + "autoFillIdentity": { + "message": "自动填充身份" + }, "generatePasswordCopied": { "message": "生成密码(并复制)" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "无匹配的登录项目" }, + "noCards": { + "message": "无支付卡" + }, + "noIdentities": { + "message": "无身份" + }, + "addLoginMenu": { + "message": "添加登录项目" + }, + "addCardMenu": { + "message": "添加支付卡" + }, + "addIdentityMenu": { + "message": "添加身份" + }, "unlockVaultMenu": { "message": "解锁您的密码库" }, @@ -850,7 +874,7 @@ "message": "使用此功能需要高级会员资格。" }, "enterVerificationCodeApp": { - "message": "请输入您的验证器应用中的 6 位验证码。" + "message": "请输入您的验证器应用中的 6 位数验证码。" }, "enterVerificationCodeEmail": { "message": "请输入发送给电子邮件 $EMAIL$ 的 6 位数验证码。", @@ -2117,7 +2141,7 @@ } }, "loginWithMasterPassword": { - "message": "主密码登录" + "message": "使用主密码登录" }, "loggingInAs": { "message": "正登录为" @@ -2132,7 +2156,7 @@ "message": "记住电子邮件地址" }, "loginWithDevice": { - "message": "设备登录" + "message": "使用设备登录" }, "loginWithDeviceEnabledInfo": { "message": "设备登录必须在 Bitwarden 应用程序的设置中启用。需要其他登录选项吗?" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 58ca526bc68..b3368beb188 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -91,6 +91,15 @@ "autoFill": { "message": "自動填入" }, + "autoFillLogin": { + "message": "Auto-fill login" + }, + "autoFillCard": { + "message": "Auto-fill card" + }, + "autoFillIdentity": { + "message": "Auto-fill identity" + }, "generatePasswordCopied": { "message": "產生及複製密碼" }, @@ -100,6 +109,21 @@ "noMatchingLogins": { "message": "無符合的登入資料" }, + "noCards": { + "message": "No cards" + }, + "noIdentities": { + "message": "No identities" + }, + "addLoginMenu": { + "message": "Add login" + }, + "addCardMenu": { + "message": "Add card" + }, + "addIdentityMenu": { + "message": "Add identity" + }, "unlockVaultMenu": { "message": "解鎖您的密碼庫" }, diff --git a/apps/browser/store/locales/az/copy.resx b/apps/browser/store/locales/az/copy.resx index 677ad41b6bc..cb05f8e5d9e 100644 --- a/apps/browser/store/locales/az/copy.resx +++ b/apps/browser/store/locales/az/copy.resx @@ -139,7 +139,7 @@ Bitwarden, parolları iş yoldaşlarınızla təhlükəsiz paylaşa bilməyiniz Nəyə görə Bitwarden-i seçməliyik: Yüksək səviyyə şifrələmə -Parollarınız qabaqcıl bir ucdan digərinə kimi şifrələmə (AES-256 bit, salted hashtag və PBKDF2 SHA-256) ilə qorunur, beləcə verilənlərinizin güvənli və gizli qalmasını təmin edir. +Parollarınız qabaqcıl ucdan-uca şifrələmə (AES-256 bit, salted hashtag və PBKDF2 SHA-256) ilə qorunur, beləcə datanızın güvənli və gizli qalmasını təmin edir. Daxili parol yaradıcı Çox istifadə etdiyiniz hər veb sayt üçün təhlükəsizlik tələblərinə görə güclü, unikal və təsadüfi şifrələr yaradın. From ceea6ef9853dfe8368167acb32b217653fff373d Mon Sep 17 00:00:00 2001 From: Robyn MacCallum Date: Fri, 6 Oct 2023 09:32:29 -0400 Subject: [PATCH 04/10] Update text for SM billing section checkbox (#6463) --- apps/web/src/app/billing/shared/sm-subscribe.component.html | 2 +- apps/web/src/locales/en/messages.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/billing/shared/sm-subscribe.component.html b/apps/web/src/app/billing/shared/sm-subscribe.component.html index c50d77be043..62a80c2a999 100644 --- a/apps/web/src/app/billing/shared/sm-subscribe.component.html +++ b/apps/web/src/app/billing/shared/sm-subscribe.component.html @@ -40,7 +40,7 @@ - {{ "addSecretsManager" | i18n }} + {{ "subscribeToSecretsManager" | i18n }} {{ "addSecretsManagerUpgradeDesc" | i18n }} diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 34d0741ceba..651325bad6f 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7096,8 +7096,8 @@ } } }, - "addSecretsManager": { - "message": "Add Secrets Manager" + "subscribeToSecretsManager": { + "message": "Subscribe to Secrets Manager" }, "addSecretsManagerUpgradeDesc": { "message": "Add Secrets Manager to your upgraded plan to maintain access to any secrets created with your previous plan." From b05b3a29353c3814048815608dc2410480007b37 Mon Sep 17 00:00:00 2001 From: Will Martin Date: Fri, 6 Oct 2023 12:12:54 -0400 Subject: [PATCH 05/10] [PM-4230] bump Electron to v26.3.0 (#6511) --- apps/desktop/electron-builder.json | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 6f760ef2b1e..0830fabf13d 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -19,7 +19,7 @@ "**/node_modules/@bitwarden/desktop-native/index.js", "**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node" ], - "electronVersion": "24.8.5", + "electronVersion": "26.3.0", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", diff --git a/package-lock.json b/package-lock.json index c67336a74b8..2725952486d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -123,7 +123,7 @@ "cross-env": "7.0.3", "css-loader": "6.8.1", "del": "6.1.1", - "electron": "24.8.5", + "electron": "26.3.0", "electron-builder": "^23.6.0", "electron-log": "4.4.8", "electron-reload": "2.0.0-alpha.1", @@ -20179,9 +20179,9 @@ } }, "node_modules/electron": { - "version": "24.8.5", - "resolved": "https://registry.npmjs.org/electron/-/electron-24.8.5.tgz", - "integrity": "sha512-CWSF0CrD1XhxyoXUcCcEoJB8orMTHuOrkj2s87XU11vjgVJHhzhCBh9TVqhMQt7U6TtcGYa5kDIiLRekxJRaRA==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-26.3.0.tgz", + "integrity": "sha512-7ZpvSHu+jmqialSvywTZnOQZZGLqlyj+yV5HGDrEzFnMiFaXBRpbByHgoUhaExJ/8t/0xKQjKlMRAY65w+zNZQ==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/package.json b/package.json index 4ca11450d04..9d824be17cf 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "cross-env": "7.0.3", "css-loader": "6.8.1", "del": "6.1.1", - "electron": "24.8.5", + "electron": "26.3.0", "electron-builder": "^23.6.0", "electron-log": "4.4.8", "electron-reload": "2.0.0-alpha.1", From 21fef9d38d7519abf3b10019c125e840c9d268f3 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:06:40 +1000 Subject: [PATCH 06/10] [AC-1453] Update TrialInitiationModule with new standalone component (#6448) * import new standalone components directly into TrialInitiationModule instead of importing and re-exporting through LooseComponentsModule --- .../auth/trial-initiation/trial-initiation.module.ts | 6 ++++-- apps/web/src/app/shared/loose-components.module.ts | 11 ----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts b/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts index 222ad94923c..426ce3e2e7c 100644 --- a/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts +++ b/apps/web/src/app/auth/trial-initiation/trial-initiation.module.ts @@ -6,9 +6,10 @@ import { FormFieldModule } from "@bitwarden/components"; import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module"; import { RegisterFormModule } from "../../auth/register-form/register-form.module"; +import { PaymentComponent, TaxInfoComponent } from "../../billing"; import { BillingComponent } from "../../billing/accounts/trial-initiation/billing.component"; import { EnvironmentSelectorModule } from "../../components/environment-selector/environment-selector.module"; -import { LooseComponentsModule, SharedModule } from "../../shared"; +import { SharedModule } from "../../shared"; import { ConfirmationDetailsComponent } from "./confirmation-details.component"; import { AbmEnterpriseContentComponent } from "./content/abm-enterprise-content.component"; @@ -37,8 +38,9 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul FormFieldModule, RegisterFormModule, OrganizationCreateModule, - LooseComponentsModule, EnvironmentSelectorModule, + PaymentComponent, + TaxInfoComponent, ], declarations: [ TrialInitiationComponent, diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 86c0da0d908..6efaf3653da 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -51,7 +51,6 @@ import { UpdatePasswordComponent } from "../auth/update-password.component"; import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component"; import { VerifyEmailTokenComponent } from "../auth/verify-email-token.component"; import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.component"; -import { BillingSharedModule } from "../billing/shared"; import { DynamicAvatarComponent } from "../components/dynamic-avatar.component"; import { SelectableAvatarComponent } from "../components/selectable-avatar.component"; import { FooterComponent } from "../layouts/footer.component"; @@ -107,11 +106,6 @@ import { SharedModule } from "./shared.module"; EnvironmentSelectorModule, AccountFingerprintComponent, PasswordCalloutComponent, - - // Temporary export to be removed in AC-1453 - // Import PaymentComponent and TaxInfoComponent directly into TrialIniationComponent - // and remove BillingSharedModule here - BillingSharedModule, ], declarations: [ AcceptEmergencyComponent, @@ -282,11 +276,6 @@ import { SharedModule } from "./shared.module"; VerifyEmailTokenComponent, VerifyRecoverDeleteComponent, LowKdfComponent, - - // Temporary export to be removed in AC-1453 - // Import PaymentComponent and TaxInfoComponent directly into TrialIniationComponent - // and remove BillingSharedModule here - BillingSharedModule, ], }) export class LooseComponentsModule {} From 320c1a59702fbf7a8cad10010505b8c51b9689c1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 12:29:22 +0200 Subject: [PATCH 07/10] Update dependency postcss to v8.4.31 [SECURITY] (#6530) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2725952486d..9ab91abac71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -154,7 +154,7 @@ "mini-css-extract-plugin": "2.7.6", "node-ipc": "9.2.1", "pkg": "5.8.1", - "postcss": "8.4.27", + "postcss": "8.4.31", "postcss-loader": "7.3.3", "prettier": "2.8.8", "prettier-plugin-tailwindcss": "0.3.0", @@ -32902,9 +32902,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index 9d824be17cf..2cd1718c1e5 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "mini-css-extract-plugin": "2.7.6", "node-ipc": "9.2.1", "pkg": "5.8.1", - "postcss": "8.4.27", + "postcss": "8.4.31", "postcss-loader": "7.3.3", "prettier": "2.8.8", "prettier-plugin-tailwindcss": "0.3.0", From 3b803f62c58d644ee973609915ef6a1099558cc5 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Mon, 9 Oct 2023 10:29:50 -0400 Subject: [PATCH 08/10] [PM-4083] Fix case of misused promise (#6443) * fix misused promise * await resolution of totpService.getCode --- .../browser/src/autofill/services/autofill.service.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index ee233c42524..aca72562287 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -148,7 +148,7 @@ export default class AutofillService implements AutofillServiceInterface { throw new Error("Nothing to auto-fill."); } - let totpPromise: Promise = null; + let totp: string | null = null; const canAccessPremium = await this.stateService.getCanAccessPremium(); const defaultUriMatch = (await this.stateService.getDefaultUriMatch()) ?? UriMatchType.Domain; @@ -205,15 +205,14 @@ export default class AutofillService implements AutofillServiceInterface { if ( options.cipher.type !== CipherType.Login || - // eslint-disable-next-line @typescript-eslint/no-misused-promises - totpPromise || + totp !== null || !options.cipher.login.totp || (!canAccessPremium && !options.cipher.organizationUseTotp) ) { return; } - totpPromise = this.stateService.getDisableAutoTotpCopy().then((disabled) => { + totp = await this.stateService.getDisableAutoTotpCopy().then((disabled) => { if (!disabled) { return this.totpService.getCode(options.cipher.login.totp); } @@ -224,8 +223,8 @@ export default class AutofillService implements AutofillServiceInterface { if (didAutofill) { this.eventCollectionService.collect(EventType.Cipher_ClientAutofilled, options.cipher.id); - if (totpPromise != null) { - return await totpPromise; + if (totp !== null) { + return totp; } else { return null; } From b2aa33f5a372c54d23da0bbae59261a937b7d4b3 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 9 Oct 2023 11:55:36 -0400 Subject: [PATCH 09/10] [PM-4194] refactor vault api implementation for federated users (#6519) * refactor vault api implementation * remove extra new line * user type context * refactor getK1 * simplify get k1 more --- .../lastpass/access/federated-user-context.ts | 6 + .../{user-type.ts => user-type-context.ts} | 33 ++-- .../src/importers/lastpass/access/vault.ts | 164 ++++++++++++++---- 3 files changed, 160 insertions(+), 43 deletions(-) create mode 100644 libs/importer/src/importers/lastpass/access/federated-user-context.ts rename libs/importer/src/importers/lastpass/access/{user-type.ts => user-type-context.ts} (59%) diff --git a/libs/importer/src/importers/lastpass/access/federated-user-context.ts b/libs/importer/src/importers/lastpass/access/federated-user-context.ts new file mode 100644 index 00000000000..7f56a695d95 --- /dev/null +++ b/libs/importer/src/importers/lastpass/access/federated-user-context.ts @@ -0,0 +1,6 @@ +export class FederatedUserContext { + username: string; + idpUserInfo: any; + accessToken: string; + idToken: string; +} diff --git a/libs/importer/src/importers/lastpass/access/user-type.ts b/libs/importer/src/importers/lastpass/access/user-type-context.ts similarity index 59% rename from libs/importer/src/importers/lastpass/access/user-type.ts rename to libs/importer/src/importers/lastpass/access/user-type-context.ts index 8321cfa7363..f2629d59516 100644 --- a/libs/importer/src/importers/lastpass/access/user-type.ts +++ b/libs/importer/src/importers/lastpass/access/user-type-context.ts @@ -1,27 +1,17 @@ -export class UserType { - /* - Type values - 0 = Master Password - 3 = Federated - */ - type: number; +export class UserTypeContext { + type: Type; IdentityProviderGUID: string; IdentityProviderURL: string; OpenIDConnectAuthority: string; OpenIDConnectClientId: string; CompanyId: number; - /* - Provider Values - 0 = LastPass - 2 = Okta - */ - Provider: number; + Provider: Provider; PkceEnabled: boolean; IsPasswordlessEnabled: boolean; isFederated(): boolean { return ( - this.type === 3 && + this.type === Type.Federated && this.hasValue(this.IdentityProviderURL) && this.hasValue(this.OpenIDConnectAuthority) && this.hasValue(this.OpenIDConnectClientId) @@ -32,3 +22,18 @@ export class UserType { return str != null && str.trim() !== ""; } } + +export enum Provider { + Azure = 0, + OktaAuthServer = 1, + OktaNoAuthServer = 2, + Google = 3, + PingOne = 4, + OneLogin = 5, +} + +export enum Type { + MasterPassword = 0, + // Not sure what Types 1 and 2 are? + Federated = 3, +} diff --git a/libs/importer/src/importers/lastpass/access/vault.ts b/libs/importer/src/importers/lastpass/access/vault.ts index 6cbee8028ca..157965804c2 100644 --- a/libs/importer/src/importers/lastpass/access/vault.ts +++ b/libs/importer/src/importers/lastpass/access/vault.ts @@ -1,3 +1,4 @@ +import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { HttpStatusCode } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -6,19 +7,24 @@ import { Account } from "./account"; import { Client } from "./client"; import { ClientInfo } from "./client-info"; import { CryptoUtils } from "./crypto-utils"; +import { FederatedUserContext } from "./federated-user-context"; import { Parser } from "./parser"; import { ParserOptions } from "./parser-options"; import { RestClient } from "./rest-client"; import { Ui } from "./ui"; -import { UserType } from "./user-type"; +import { Provider, UserTypeContext } from "./user-type-context"; export class Vault { accounts: Account[]; + userType: UserTypeContext; private client: Client; private cryptoUtils: CryptoUtils; - constructor(private cryptoFunctionService: CryptoFunctionService) { + constructor( + private cryptoFunctionService: CryptoFunctionService, + private tokenService: TokenService + ) { this.cryptoUtils = new CryptoUtils(cryptoFunctionService); const parser = new Parser(cryptoFunctionService, this.cryptoUtils); this.client = new Client(parser, this.cryptoUtils); @@ -35,24 +41,25 @@ export class Vault { } async openFederated( - username: string, - k1: string, - k2: string, + federatedUser: FederatedUserContext, clientInfo: ClientInfo, ui: Ui, parserOptions: ParserOptions = ParserOptions.default ): Promise { - const k1Arr = Utils.fromByteStringToArray(k1); - const k2Arr = Utils.fromB64ToArray(k2); + if (federatedUser == null) { + throw "Federated user context is not set."; + } + const k1 = await this.getK1(federatedUser); + const k2 = await this.getK2(federatedUser); const hiddenPasswordArr = await this.cryptoFunctionService.hash( - this.cryptoUtils.ExclusiveOr(k1Arr, k2Arr), + this.cryptoUtils.ExclusiveOr(k1, k2), "sha256" ); const hiddenPassword = Utils.fromBufferToB64(hiddenPasswordArr); - await this.open(username, hiddenPassword, clientInfo, ui, parserOptions); + await this.open(federatedUser.username, hiddenPassword, clientInfo, ui, parserOptions); } - async getUserType(username: string): Promise { + async setUserTypeContext(username: string) { const lowercaseUsername = username.toLowerCase(); const rest = new RestClient(); rest.baseUrl = "https://lastpass.com"; @@ -60,35 +67,134 @@ export class Vault { const response = await rest.get(endpoint); if (response.status === HttpStatusCode.Ok) { const json = await response.json(); - const userType = new UserType(); - userType.CompanyId = json.CompanyId; - userType.IdentityProviderGUID = json.IdentityProviderGUID; - userType.IdentityProviderURL = json.IdentityProviderURL; - userType.IsPasswordlessEnabled = json.IsPasswordlessEnabled; - userType.OpenIDConnectAuthority = json.OpenIDConnectAuthority; - userType.OpenIDConnectClientId = json.OpenIDConnectClientId; - userType.PkceEnabled = json.PkceEnabled; - userType.Provider = json.Provider; - userType.type = json.type; - return userType; + this.userType = new UserTypeContext(); + this.userType.CompanyId = json.CompanyId; + this.userType.IdentityProviderGUID = json.IdentityProviderGUID; + this.userType.IdentityProviderURL = json.IdentityProviderURL; + this.userType.IsPasswordlessEnabled = json.IsPasswordlessEnabled; + this.userType.OpenIDConnectAuthority = json.OpenIDConnectAuthority; + this.userType.OpenIDConnectClientId = json.OpenIDConnectClientId; + this.userType.PkceEnabled = json.PkceEnabled; + this.userType.Provider = json.Provider; + this.userType.type = json.type; } throw "Cannot determine LastPass user type."; } - async getIdentityProviderKey(userType: UserType, idToken: string): Promise { - if (!userType.isFederated()) { - throw "Cannot get identity provider key for a LastPass user that is not federated."; + private async getK1(federatedUser: FederatedUserContext): Promise { + if (this.userType == null) { + throw "User type is not set."; } + + if (!this.userType.isFederated()) { + throw "Cannot get k1 for LastPass user that is not federated."; + } + + if (federatedUser == null) { + throw "Federated user is not set."; + } + + let k1: Uint8Array = null; + if (federatedUser.idpUserInfo?.LastPassK1 !== null) { + return Utils.fromByteStringToArray(federatedUser.idpUserInfo.LastPassK1); + } else if (this.userType.Provider === Provider.Azure) { + k1 = await this.getK1Azure(federatedUser); + } else if (this.userType.Provider === Provider.Google) { + k1 = await this.getK1Google(federatedUser); + } else { + const b64Encoded = this.userType.Provider === Provider.PingOne; + k1 = this.getK1FromAccessToken(federatedUser, b64Encoded); + } + + if (k1 !== null) { + return k1; + } + + throw "Cannot get k1."; + } + + private async getK1Azure(federatedUser: FederatedUserContext) { + // Query the Graph API for the k1 field const rest = new RestClient(); - rest.baseUrl = userType.IdentityProviderURL; + rest.baseUrl = "https://graph.microsoft.com"; + const response = await rest.get( + "v1.0/me?$select=id,displayName,mail&$expand=extensions", + new Map([["Authorization", "Bearer " + federatedUser.accessToken]]) + ); + if (response.status === HttpStatusCode.Ok) { + const json = await response.json(); + const k1 = json?.extensions?.LastPassK1 as string; + if (k1 !== null) { + return Utils.fromB64ToArray(k1); + } + } + return null; + } + + private async getK1Google(federatedUser: FederatedUserContext) { + // Query Google Drive for the k1.lp file + const accessTokenAuthHeader = new Map([ + ["Authorization", "Bearer " + federatedUser.accessToken], + ]); + const rest = new RestClient(); + rest.baseUrl = "https://content.googleapis.com"; + const response = await rest.get( + "drive/v3/files?pageSize=1" + + "&q=name%20%3D%20%27k1.lp%27" + + "&spaces=appDataFolder" + + "&fields=nextPageToken%2C%20files(id%2C%20name)", + accessTokenAuthHeader + ); + if (response.status === HttpStatusCode.Ok) { + const json = await response.json(); + const files = json?.files as any[]; + if (files !== null && files.length > 0 && files[0].id != null && files[0].name === "k1.lp") { + // Open the k1.lp file + rest.baseUrl = "https://www.googleapis.com"; + const response = await rest.get( + "drive/v3/files/" + files[0].id + "?alt=media", + accessTokenAuthHeader + ); + if (response.status === HttpStatusCode.Ok) { + const k1 = await response.text(); + return Utils.fromB64ToArray(k1); + } + } + } + return null; + } + + private getK1FromAccessToken(federatedUser: FederatedUserContext, b64: boolean) { + const decodedAccessToken = this.tokenService.decodeToken(federatedUser.accessToken); + const k1 = decodedAccessToken?.LastPassK1 as string; + if (k1 !== null) { + return b64 ? Utils.fromB64ToArray(k1) : Utils.fromByteStringToArray(k1); + } + return null; + } + + private async getK2(federatedUser: FederatedUserContext): Promise { + if (this.userType == null) { + throw "User type is not set."; + } + + if (!this.userType.isFederated()) { + throw "Cannot get k2 for LastPass user that is not federated."; + } + + const rest = new RestClient(); + rest.baseUrl = this.userType.IdentityProviderURL; const response = await rest.postJson("federatedlogin/api/v1/getkey", { - company_id: userType.CompanyId, - id_token: idToken, + company_id: this.userType.CompanyId, + id_token: federatedUser.idToken, }); if (response.status === HttpStatusCode.Ok) { const json = await response.json(); - return json["k2"] as string; + const k2 = json?.k2 as string; + if (k2 !== null) { + return Utils.fromB64ToArray(k2); + } } - throw "Cannot get identity provider key from LastPass."; + throw "Cannot get k2."; } } From 725ee08640eca13f9278ff49fc1dfcd0e8efc2c0 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 10 Oct 2023 15:10:26 +0200 Subject: [PATCH 10/10] [PM-2014] Passkey registration (#5396) * [PM-2014] feat: scaffold new fido2 login component and module * [PM-1024] feat: add content to login settings component * [PM-1024] feat: add badge and button aria label * [PM-2014] feat: create new dialog * feat: add ability to remove form field bottom margin (cherry picked from commit 05925ff77ed47f3865c2aecade8271390d9e2fa6) * [PM-2014] feat: disable dialog close button * [PM-2014] feat: implement mocked failing wizard flow * [PM-2014] feat: add icons and other content * [PM-2014] feat: change wording to "creating" password * [PM-2014] feat: add new auth and auth core modules * [PM-2014] chore: move fido2-login-settings to auth module * [PM-2014] chore: expose using barrel files * [PM-2014] feat: fetch webauthn challenge * [PM-2014] chore: refactor api logic into new api service and move ui logic into existing service * [PM-2014] feat: add tests for new credential options * [PM-2014] feat: return undefined when credential creation fails * [PM-2014] feat: implement credential creation * [PM-2014] feat: add passkey naming ui * [PM-2014] feat: add support for creation token * [PM-2014] feat: implement credential saving * [PM-2014] feat: Basic list of credentials * [PM-2014] feat: improve async data loading * [PM-2014] feat: finish up list UI * [PM-2014] fix: loading state not being set properly * [PM-2014] feat: improve aria labels * [PM-2014] feat: show toast on passkey saved * [PM-2014] feat: add delete dialog * [PM-2014] feat: implement deletion without user verification * [PM-2014] feat: add user verification to delete * [PM-2014] feat: change to danger button * [PM-2014] feat: show `save` if passkeys already exist * [PM-2014] feat: add passkey limit * [PM-2014] feat: improve error on delete * [PM-2014] feat: add support for feature flag * [PM-2014] feat: update copy * [PM-2014] feat: reduce remove button margin * [PM-2014] feat: refactor submit method * [PM-2014] feat: autofocus fields * [PM-2014] fix: move error handling to components After discussing it with Jake we decided that following convention was best. * [PM-2014] feat: change toast depending on existing passkeys * [PM-2014] chore: rename everything from `fido2` to `webauthn` * [PM-2014] fix: `CoreAuthModule` duplicate import * [PM-2014] feat: change to new figma design `Encryption not supported` * [PM-2014] fix: add missing href * [PM-2014] fix: misaligned badge * [PM-2014] chore: remove whitespace * [PM-2014] fix: dialog close bug * [PM-2014] fix: badge alignment not applying properly * [PM-2014] fix: remove redundant align class * [PM-2014] chore: move CoreAuthModule to AuthModule * [PM-2014] feat: create new settings module * [PM-2014] feat: move change password component to settings module * [PM-2014] chore: tweak loose components recommendation * [PM-2014] fix: remove deprecated pattern * [PM-2014] chore: rename everything to `WebauthnLogin` to follow new naming scheme * [PM-2014] chore: document requests and responses * [PM-2014] fix: remove `undefined` * [PM-2014] fix: clarify webauthn login service * [PM-2014] fix: use `getCredentials$()` * [PM-2014] fix: badge alignment using important statement * [PM-2014] fix: remove sm billing flag * [PM-2014] fix: `CoreAuthModule` double import * [PM-2014] fix: unimported component (issue due to conflict with master) * [PM-2014] fix: unawaited promise bug --- apps/web/src/app/auth/auth.module.ts | 12 ++ apps/web/src/app/auth/core/core.module.ts | 15 ++ apps/web/src/app/auth/core/index.ts | 2 + apps/web/src/app/auth/core/services/index.ts | 1 + .../core/services/webauthn-login/index.ts | 1 + .../request/save-credential.request.ts | 18 ++ ...uthn-login-attestation-response.request.ts | 27 +++ ...hn-login-authenticator-response.request.ts | 19 ++ ...ogin-credential-create-options.response.ts | 22 +++ .../webauthn-login-credential.response.ts | 17 ++ .../webauthn-login-api.service.ts | 40 ++++ .../webauthn-login.service.spec.ts | 63 +++++++ .../webauthn-login/webauthn-login.service.ts | 109 +++++++++++ .../views/credential-create-options.view.ts | 5 + .../core/views/webauth-credential.view.ts | 5 + apps/web/src/app/auth/index.ts | 2 + .../settings/change-password.component.html | 13 +- .../settings/change-password.component.ts | 15 +- .../src/app/auth/settings/settings.module.ts | 16 ++ .../create-credential-dialog.component.html | 70 +++++++ .../create-credential-dialog.component.ts | 178 ++++++++++++++++++ .../create-passkey-failed.icon.ts | 28 +++ .../create-passkey.icon.ts | 26 +++ .../delete-credential-dialog.component.html | 34 ++++ .../delete-credential-dialog.component.ts | 95 ++++++++++ .../settings/webauthn-login-settings/index.ts | 1 + .../webauthn-login-settings.component.html | 71 +++++++ .../webauthn-login-settings.component.ts | 72 +++++++ .../webauthn-login-settings.module.ts | 19 ++ apps/web/src/app/oss.module.ts | 2 + .../src/app/shared/loose-components.module.ts | 3 - apps/web/src/locales/en/messages.json | 82 ++++++++ libs/common/src/enums/feature-flag.enum.ts | 1 + .../directives/dialog-close.directive.ts | 14 +- 34 files changed, 1088 insertions(+), 10 deletions(-) create mode 100644 apps/web/src/app/auth/auth.module.ts create mode 100644 apps/web/src/app/auth/core/core.module.ts create mode 100644 apps/web/src/app/auth/core/index.ts create mode 100644 apps/web/src/app/auth/core/services/index.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/index.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/request/save-credential.request.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-attestation-response.request.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-authenticator-response.request.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential-create-options.response.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential.response.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts create mode 100644 apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts create mode 100644 apps/web/src/app/auth/core/views/credential-create-options.view.ts create mode 100644 apps/web/src/app/auth/core/views/webauth-credential.view.ts create mode 100644 apps/web/src/app/auth/index.ts create mode 100644 apps/web/src/app/auth/settings/settings.module.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey-failed.icon.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey.icon.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/index.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.html create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.ts create mode 100644 apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts diff --git a/apps/web/src/app/auth/auth.module.ts b/apps/web/src/app/auth/auth.module.ts new file mode 100644 index 00000000000..49be17aa264 --- /dev/null +++ b/apps/web/src/app/auth/auth.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from "@angular/core"; + +import { CoreAuthModule } from "./core"; +import { SettingsModule } from "./settings/settings.module"; + +@NgModule({ + imports: [CoreAuthModule, SettingsModule], + declarations: [], + providers: [], + exports: [SettingsModule], +}) +export class AuthModule {} diff --git a/apps/web/src/app/auth/core/core.module.ts b/apps/web/src/app/auth/core/core.module.ts new file mode 100644 index 00000000000..e196b1c3d76 --- /dev/null +++ b/apps/web/src/app/auth/core/core.module.ts @@ -0,0 +1,15 @@ +import { NgModule, Optional, SkipSelf } from "@angular/core"; + +import { WebauthnLoginApiService } from "./services/webauthn-login/webauthn-login-api.service"; +import { WebauthnLoginService } from "./services/webauthn-login/webauthn-login.service"; + +@NgModule({ + providers: [WebauthnLoginService, WebauthnLoginApiService], +}) +export class CoreAuthModule { + constructor(@Optional() @SkipSelf() parentModule?: CoreAuthModule) { + if (parentModule) { + throw new Error("CoreAuthModule is already loaded. Import it in AuthModule only"); + } + } +} diff --git a/apps/web/src/app/auth/core/index.ts b/apps/web/src/app/auth/core/index.ts new file mode 100644 index 00000000000..3d2d739adf9 --- /dev/null +++ b/apps/web/src/app/auth/core/index.ts @@ -0,0 +1,2 @@ +export * from "./services"; +export * from "./core.module"; diff --git a/apps/web/src/app/auth/core/services/index.ts b/apps/web/src/app/auth/core/services/index.ts new file mode 100644 index 00000000000..4ef20f4b97d --- /dev/null +++ b/apps/web/src/app/auth/core/services/index.ts @@ -0,0 +1 @@ +export * from "./webauthn-login"; diff --git a/apps/web/src/app/auth/core/services/webauthn-login/index.ts b/apps/web/src/app/auth/core/services/webauthn-login/index.ts new file mode 100644 index 00000000000..10dea636b8d --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/index.ts @@ -0,0 +1 @@ +export * from "./webauthn-login.service"; diff --git a/apps/web/src/app/auth/core/services/webauthn-login/request/save-credential.request.ts b/apps/web/src/app/auth/core/services/webauthn-login/request/save-credential.request.ts new file mode 100644 index 00000000000..ffd0e6cf709 --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/request/save-credential.request.ts @@ -0,0 +1,18 @@ +import { WebauthnLoginAttestationResponseRequest } from "./webauthn-login-attestation-response.request"; + +/** + * Request sent to the server to save a newly created webauthn login credential. + */ +export class SaveCredentialRequest { + /** The response recieved from the authenticator. This contains the public key */ + deviceResponse: WebauthnLoginAttestationResponseRequest; + + /** Nickname chosen by the user to identify this credential */ + name: string; + + /** + * Token required by the server to complete the creation. + * It contains encrypted information that the server needs to verify the credential. + */ + token: string; +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-attestation-response.request.ts b/apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-attestation-response.request.ts new file mode 100644 index 00000000000..4b33896290c --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-attestation-response.request.ts @@ -0,0 +1,27 @@ +import { Utils } from "@bitwarden/common/platform/misc/utils"; + +import { WebauthnLoginAuthenticatorResponseRequest } from "./webauthn-login-authenticator-response.request"; + +/** + * The response recieved from an authentiator after a successful attestation. + * This request is used to save newly created webauthn login credentials to the server. + */ +export class WebauthnLoginAttestationResponseRequest extends WebauthnLoginAuthenticatorResponseRequest { + response: { + attestationObject: string; + clientDataJson: string; + }; + + constructor(credential: PublicKeyCredential) { + super(credential); + + if (!(credential.response instanceof AuthenticatorAttestationResponse)) { + throw new Error("Invalid authenticator response"); + } + + this.response = { + attestationObject: Utils.fromBufferToB64(credential.response.attestationObject), + clientDataJson: Utils.fromBufferToB64(credential.response.clientDataJSON), + }; + } +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-authenticator-response.request.ts b/apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-authenticator-response.request.ts new file mode 100644 index 00000000000..9e332ad5381 --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/request/webauthn-login-authenticator-response.request.ts @@ -0,0 +1,19 @@ +import { Utils } from "@bitwarden/common/platform/misc/utils"; + +/** + * An abstract class that represents responses recieved from the webauthn authenticator. + * It contains data that is commonly returned during different types of authenticator interactions. + */ +export abstract class WebauthnLoginAuthenticatorResponseRequest { + id: string; + rawId: string; + type: string; + extensions: Record; + + constructor(credential: PublicKeyCredential) { + this.id = credential.id; + this.rawId = Utils.fromBufferToB64(credential.rawId); + this.type = credential.type; + this.extensions = {}; // Extensions are handled client-side + } +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential-create-options.response.ts b/apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential-create-options.response.ts new file mode 100644 index 00000000000..ce588207727 --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential-create-options.response.ts @@ -0,0 +1,22 @@ +import { ChallengeResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +/** + * Options provided by the server to be used during attestation (i.e. creation of a new webauthn credential) + */ +export class WebauthnLoginCredentialCreateOptionsResponse extends BaseResponse { + /** Options to be provided to the webauthn authenticator */ + options: ChallengeResponse; + + /** + * Contains an encrypted version of the {@link options}. + * Used by the server to validate the attestation response of newly created credentials. + */ + token: string; + + constructor(response: unknown) { + super(response); + this.options = new ChallengeResponse(this.getResponseProperty("options")); + this.token = this.getResponseProperty("token"); + } +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential.response.ts b/apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential.response.ts new file mode 100644 index 00000000000..7a7f5199e7c --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/response/webauthn-login-credential.response.ts @@ -0,0 +1,17 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +/** + * A webauthn login credential recieved from the server. + */ +export class WebauthnLoginCredentialResponse extends BaseResponse { + id: string; + name: string; + prfSupport: boolean; + + constructor(response: unknown) { + super(response); + this.id = this.getResponseProperty("id"); + this.name = this.getResponseProperty("name"); + this.prfSupport = this.getResponseProperty("prfSupport"); + } +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts new file mode 100644 index 00000000000..33e1aea369b --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from "@angular/core"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { Verification } from "@bitwarden/common/types/verification"; + +import { SaveCredentialRequest } from "./request/save-credential.request"; +import { WebauthnLoginCredentialCreateOptionsResponse } from "./response/webauthn-login-credential-create-options.response"; +import { WebauthnLoginCredentialResponse } from "./response/webauthn-login-credential.response"; + +@Injectable() +export class WebauthnLoginApiService { + constructor( + private apiService: ApiService, + private userVerificationService: UserVerificationService + ) {} + + async getCredentialCreateOptions( + verification: Verification + ): Promise { + const request = await this.userVerificationService.buildRequest(verification); + const response = await this.apiService.send("POST", "/webauthn/options", request, true, true); + return new WebauthnLoginCredentialCreateOptionsResponse(response); + } + + async saveCredential(request: SaveCredentialRequest): Promise { + await this.apiService.send("POST", "/webauthn", request, true, true); + return true; + } + + getCredentials(): Promise> { + return this.apiService.send("GET", "/webauthn", null, true, true); + } + + async deleteCredential(credentialId: string, verification: Verification): Promise { + const request = await this.userVerificationService.buildRequest(verification); + await this.apiService.send("POST", `/webauthn/${credentialId}/delete`, request, true, true); + } +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts new file mode 100644 index 00000000000..070513f19e8 --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts @@ -0,0 +1,63 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { CredentialCreateOptionsView } from "../../views/credential-create-options.view"; + +import { WebauthnLoginApiService } from "./webauthn-login-api.service"; +import { WebauthnLoginService } from "./webauthn-login.service"; + +describe("WebauthnService", () => { + let apiService!: MockProxy; + let credentials: MockProxy; + let webauthnService!: WebauthnLoginService; + + beforeAll(() => { + // Polyfill missing class + window.PublicKeyCredential = class {} as any; + window.AuthenticatorAttestationResponse = class {} as any; + apiService = mock(); + credentials = mock(); + webauthnService = new WebauthnLoginService(apiService, credentials); + }); + + describe("createCredential", () => { + it("should return undefined when navigator.credentials throws", async () => { + credentials.create.mockRejectedValue(new Error("Mocked error")); + const options = createCredentialCreateOptions(); + + const result = await webauthnService.createCredential(options); + + expect(result).toBeUndefined(); + }); + + it("should return credential when navigator.credentials does not throw", async () => { + const credential = createDeviceResponse(); + credentials.create.mockResolvedValue(credential as PublicKeyCredential); + const options = createCredentialCreateOptions(); + + const result = await webauthnService.createCredential(options); + + expect(result).toBe(credential); + }); + }); +}); + +function createCredentialCreateOptions(): CredentialCreateOptionsView { + return new CredentialCreateOptionsView(Symbol() as any, Symbol() as any); +} + +function createDeviceResponse(): PublicKeyCredential { + const credential = { + id: "dGVzdA==", + rawId: new Uint8Array([0x74, 0x65, 0x73, 0x74]), + type: "public-key", + response: { + attestationObject: new Uint8Array([0, 0, 0]), + clientDataJSON: "eyJ0ZXN0IjoidGVzdCJ9", + }, + } as any; + + Object.setPrototypeOf(credential, PublicKeyCredential.prototype); + Object.setPrototypeOf(credential.response, AuthenticatorAttestationResponse.prototype); + + return credential; +} diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts new file mode 100644 index 00000000000..760214961a7 --- /dev/null +++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts @@ -0,0 +1,109 @@ +import { Injectable, Optional } from "@angular/core"; +import { BehaviorSubject, filter, from, map, Observable, shareReplay, switchMap, tap } from "rxjs"; + +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { Verification } from "@bitwarden/common/types/verification"; + +import { CredentialCreateOptionsView } from "../../views/credential-create-options.view"; +import { WebauthnCredentialView } from "../../views/webauth-credential.view"; + +import { SaveCredentialRequest } from "./request/save-credential.request"; +import { WebauthnLoginAttestationResponseRequest } from "./request/webauthn-login-attestation-response.request"; +import { WebauthnLoginApiService } from "./webauthn-login-api.service"; + +@Injectable() +export class WebauthnLoginService { + private navigatorCredentials: CredentialsContainer; + private _refresh$ = new BehaviorSubject(undefined); + private _loading$ = new BehaviorSubject(true); + private readonly credentials$ = this._refresh$.pipe( + tap(() => this._loading$.next(true)), + switchMap(() => this.fetchCredentials$()), + tap(() => this._loading$.next(false)), + shareReplay({ bufferSize: 1, refCount: true }) + ); + + readonly loading$ = this._loading$.asObservable(); + + constructor( + private apiService: WebauthnLoginApiService, + @Optional() navigatorCredentials?: CredentialsContainer, + @Optional() private logService?: LogService + ) { + // Default parameters don't work when used with Angular DI + this.navigatorCredentials = navigatorCredentials ?? navigator.credentials; + } + + async getCredentialCreateOptions( + verification: Verification + ): Promise { + const response = await this.apiService.getCredentialCreateOptions(verification); + return new CredentialCreateOptionsView(response.options, response.token); + } + + async createCredential( + credentialOptions: CredentialCreateOptionsView + ): Promise { + const nativeOptions: CredentialCreationOptions = { + publicKey: credentialOptions.options, + }; + + try { + const response = await this.navigatorCredentials.create(nativeOptions); + if (!(response instanceof PublicKeyCredential)) { + return undefined; + } + return response; + } catch (error) { + this.logService?.error(error); + return undefined; + } + } + + async saveCredential( + credentialOptions: CredentialCreateOptionsView, + deviceResponse: PublicKeyCredential, + name: string + ) { + const request = new SaveCredentialRequest(); + request.deviceResponse = new WebauthnLoginAttestationResponseRequest(deviceResponse); + request.token = credentialOptions.token; + request.name = name; + await this.apiService.saveCredential(request); + this.refresh(); + } + + /** + * List of webauthn credentials saved on the server. + * + * **Note:** + * - Subscribing might trigger a network request if the credentials haven't been fetched yet. + * - The observable is shared and will not create unnecessary duplicate requests. + * - The observable will automatically re-fetch if the user adds or removes a credential. + * - The observable is lazy and will only fetch credentials when subscribed to. + * - Don't subscribe to this in the constructor of a long-running service, as it will keep the observable alive. + */ + getCredentials$(): Observable { + return this.credentials$; + } + + getCredential$(credentialId: string): Observable { + return this.credentials$.pipe( + map((credentials) => credentials.find((c) => c.id === credentialId)), + filter((c) => c !== undefined) + ); + } + + async deleteCredential(credentialId: string, verification: Verification): Promise { + await this.apiService.deleteCredential(credentialId, verification); + this.refresh(); + } + + private fetchCredentials$(): Observable { + return from(this.apiService.getCredentials()).pipe(map((response) => response.data)); + } + + private refresh() { + this._refresh$.next(); + } +} diff --git a/apps/web/src/app/auth/core/views/credential-create-options.view.ts b/apps/web/src/app/auth/core/views/credential-create-options.view.ts new file mode 100644 index 00000000000..29efdef5ee6 --- /dev/null +++ b/apps/web/src/app/auth/core/views/credential-create-options.view.ts @@ -0,0 +1,5 @@ +import { ChallengeResponse } from "@bitwarden/common/auth/models/response/two-factor-web-authn.response"; + +export class CredentialCreateOptionsView { + constructor(readonly options: ChallengeResponse, readonly token: string) {} +} diff --git a/apps/web/src/app/auth/core/views/webauth-credential.view.ts b/apps/web/src/app/auth/core/views/webauth-credential.view.ts new file mode 100644 index 00000000000..ff5a9c692df --- /dev/null +++ b/apps/web/src/app/auth/core/views/webauth-credential.view.ts @@ -0,0 +1,5 @@ +export class WebauthnCredentialView { + id: string; + name: string; + prfSupport: boolean; +} diff --git a/apps/web/src/app/auth/index.ts b/apps/web/src/app/auth/index.ts new file mode 100644 index 00000000000..fb09223bd9e --- /dev/null +++ b/apps/web/src/app/auth/index.ts @@ -0,0 +1,2 @@ +export * from "./auth.module"; +export * from "./core"; diff --git a/apps/web/src/app/auth/settings/change-password.component.html b/apps/web/src/app/auth/settings/change-password.component.html index b3ad78168d9..37a4ad5b59a 100644 --- a/apps/web/src/app/auth/settings/change-password.component.html +++ b/apps/web/src/app/auth/settings/change-password.component.html @@ -6,7 +6,14 @@ -
+
@@ -118,3 +125,7 @@ {{ "changeMasterPassword" | i18n }} + + diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index 9ed8227316c..958582eb0a7 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, Observable } from "rxjs"; import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -11,12 +11,13 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { EmergencyAccessStatusType } from "@bitwarden/common/auth/enums/emergency-access-status-type"; import { EmergencyAccessUpdateRequest } from "@bitwarden/common/auth/models/request/emergency-access-update.request"; import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { UpdateKeyRequest } from "@bitwarden/common/models/request/update-key.request"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -50,6 +51,8 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { checkForBreaches = true; characterMinimumMessage = ""; + protected showWebauthnLoginSettings$: Observable; + constructor( i18nService: I18nService, cryptoService: CryptoService, @@ -65,13 +68,13 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { private apiService: ApiService, private sendService: SendService, private organizationService: OrganizationService, - private keyConnectorService: KeyConnectorService, private router: Router, private organizationApiService: OrganizationApiServiceAbstraction, private organizationUserService: OrganizationUserService, dialogService: DialogService, private userVerificationService: UserVerificationService, - private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction + private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + private configService: ConfigServiceAbstraction ) { super( i18nService, @@ -86,6 +89,10 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { } async ngOnInit() { + this.showWebauthnLoginSettings$ = this.configService.getFeatureFlag$( + FeatureFlag.PasswordlessLogin + ); + if (!(await this.userVerificationService.hasMasterPassword())) { this.router.navigate(["/settings/security/two-factor"]); } diff --git a/apps/web/src/app/auth/settings/settings.module.ts b/apps/web/src/app/auth/settings/settings.module.ts new file mode 100644 index 00000000000..282524d07e4 --- /dev/null +++ b/apps/web/src/app/auth/settings/settings.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from "@angular/core"; + +import { PasswordCalloutComponent } from "@bitwarden/auth"; + +import { SharedModule } from "../../shared"; + +import { ChangePasswordComponent } from "./change-password.component"; +import { WebauthnLoginSettingsModule } from "./webauthn-login-settings"; + +@NgModule({ + imports: [SharedModule, WebauthnLoginSettingsModule, PasswordCalloutComponent], + declarations: [ChangePasswordComponent], + providers: [], + exports: [WebauthnLoginSettingsModule, ChangePasswordComponent], +}) +export class SettingsModule {} diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html new file mode 100644 index 00000000000..57a2c545ca1 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html @@ -0,0 +1,70 @@ +
+ + {{ "loginWithPasskey" | i18n }} + {{ "newPasskey" | i18n }} + + + +

+ {{ "passkeyEnterMasterPassword" | i18n }} +

+ + {{ "masterPassword" | i18n }} + + + {{ "confirmIdentity" | i18n }} + +
+ +
+ +

{{ "creatingPasskeyLoading" | i18n }}

+

{{ "creatingPasskeyLoadingInfo" | i18n }}

+
+ +
+ +

{{ "errorCreatingPasskey" | i18n }}

+

{{ "errorCreatingPasskeyInfo" | i18n }}

+
+ +
+

{{ "passkeySuccessfullyCreated" | i18n }}

+

+ {{ "customPasskeyNameInfo" | i18n }} +

+ + {{ "customName" | i18n }} + + {{ + "charactersCurrentAndMaximum" + | i18n : formGroup.value.credentialNaming.name.length : NameMaxCharacters + }} + +
+
+ + + + +
+
diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts new file mode 100644 index 00000000000..5c93d6f25e2 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.ts @@ -0,0 +1,178 @@ +import { DialogConfig, DialogRef } from "@angular/cdk/dialog"; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, Validators } from "@angular/forms"; +import { firstValueFrom, map, Observable } from "rxjs"; + +import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; + +import { WebauthnLoginService } from "../../../core"; +import { CredentialCreateOptionsView } from "../../../core/views/credential-create-options.view"; + +import { CreatePasskeyFailedIcon } from "./create-passkey-failed.icon"; +import { CreatePasskeyIcon } from "./create-passkey.icon"; + +export enum CreateCredentialDialogResult { + Success, +} + +type Step = + | "userVerification" + | "credentialCreation" + | "credentialCreationFailed" + | "credentialNaming"; + +@Component({ + templateUrl: "create-credential-dialog.component.html", +}) +export class CreateCredentialDialogComponent implements OnInit { + protected readonly NameMaxCharacters = 50; + protected readonly CreateCredentialDialogResult = CreateCredentialDialogResult; + protected readonly Icons = { CreatePasskeyIcon, CreatePasskeyFailedIcon }; + + protected currentStep: Step = "userVerification"; + protected formGroup = this.formBuilder.group({ + userVerification: this.formBuilder.group({ + masterPassword: ["", [Validators.required]], + }), + credentialNaming: this.formBuilder.group({ + name: ["", Validators.maxLength(50)], + }), + }); + protected credentialOptions?: CredentialCreateOptionsView; + protected deviceResponse?: PublicKeyCredential; + protected hasPasskeys$?: Observable; + + constructor( + private formBuilder: FormBuilder, + private dialogRef: DialogRef, + private webauthnService: WebauthnLoginService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private logService: LogService + ) {} + + ngOnInit(): void { + this.hasPasskeys$ = this.webauthnService + .getCredentials$() + .pipe(map((credentials) => credentials.length > 0)); + } + + protected submit = async () => { + this.dialogRef.disableClose = true; + + try { + switch (this.currentStep) { + case "userVerification": + return await this.submitUserVerification(); + case "credentialCreationFailed": + return await this.submitCredentialCreationFailed(); + case "credentialCreation": + return await this.submitCredentialCreation(); + case "credentialNaming": + return await this.submitCredentialNaming(); + } + } finally { + this.dialogRef.disableClose = false; + } + }; + + protected async submitUserVerification() { + this.formGroup.controls.userVerification.markAllAsTouched(); + if (this.formGroup.controls.userVerification.invalid) { + return; + } + + try { + this.credentialOptions = await this.webauthnService.getCredentialCreateOptions({ + type: VerificationType.MasterPassword, + secret: this.formGroup.value.userVerification.masterPassword, + }); + } catch (error) { + if (error instanceof ErrorResponse && error.statusCode === 400) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("error"), + this.i18nService.t("invalidMasterPassword") + ); + } else { + this.logService?.error(error); + this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + } + return; + } + + this.currentStep = "credentialCreation"; + await this.submitCredentialCreation(); + } + + protected async submitCredentialCreation() { + this.deviceResponse = await this.webauthnService.createCredential(this.credentialOptions); + if (this.deviceResponse === undefined) { + this.currentStep = "credentialCreationFailed"; + return; + } + + this.currentStep = "credentialNaming"; + } + + protected async submitCredentialCreationFailed() { + this.currentStep = "credentialCreation"; + await this.submitCredentialCreation(); + } + + protected async submitCredentialNaming() { + this.formGroup.controls.credentialNaming.markAllAsTouched(); + if (this.formGroup.controls.credentialNaming.invalid) { + return; + } + + const name = this.formGroup.value.credentialNaming.name; + try { + await this.webauthnService.saveCredential( + this.credentialOptions, + this.deviceResponse, + this.formGroup.value.credentialNaming.name + ); + } catch (error) { + this.logService?.error(error); + this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + return; + } + + if (await firstValueFrom(this.hasPasskeys$)) { + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("passkeySaved", name) + ); + } else { + this.platformUtilsService.showToast( + "success", + null, + this.i18nService.t("loginWithPasskeyEnabled") + ); + } + + this.dialogRef.close(CreateCredentialDialogResult.Success); + } +} + +/** + * Strongly typed helper to open a CreateCredentialDialog + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param config Configuration for the dialog + */ +export const openCreateCredentialDialog = ( + dialogService: DialogService, + config: DialogConfig +) => { + return dialogService.open( + CreateCredentialDialogComponent, + config + ); +}; diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey-failed.icon.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey-failed.icon.ts new file mode 100644 index 00000000000..39a2389c5a9 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey-failed.icon.ts @@ -0,0 +1,28 @@ +import { svgIcon } from "@bitwarden/components"; + +export const CreatePasskeyFailedIcon = svgIcon` + + + + + + + + + + + +`; diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey.icon.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey.icon.ts new file mode 100644 index 00000000000..c0e984bbee2 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-passkey.icon.ts @@ -0,0 +1,26 @@ +import { svgIcon } from "@bitwarden/components"; + +export const CreatePasskeyIcon = svgIcon` + + + + + + + + + + +`; diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html new file mode 100644 index 00000000000..4cfdbbcf7fe --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.html @@ -0,0 +1,34 @@ +
+ + {{ "removePasskey" | i18n }} + {{ + credential.name + }} + + + + + + + +

{{ "removePasskeyInfo" | i18n }}

+ + + {{ "masterPassword" | i18n }} + + + {{ "confirmIdentity" | i18n }} + +
+
+ + + + +
+
diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts new file mode 100644 index 00000000000..7cb03238392 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/delete-credential-dialog/delete-credential-dialog.component.ts @@ -0,0 +1,95 @@ +import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; +import { FormBuilder, Validators } from "@angular/forms"; +import { Subject, takeUntil } from "rxjs"; + +import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; + +import { WebauthnLoginService } from "../../../core"; +import { WebauthnCredentialView } from "../../../core/views/webauth-credential.view"; + +export interface DeleteCredentialDialogParams { + credentialId: string; +} + +@Component({ + templateUrl: "delete-credential-dialog.component.html", +}) +export class DeleteCredentialDialogComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); + + protected formGroup = this.formBuilder.group({ + masterPassword: ["", [Validators.required]], + }); + protected credential?: WebauthnCredentialView; + + constructor( + @Inject(DIALOG_DATA) private params: DeleteCredentialDialogParams, + private formBuilder: FormBuilder, + private dialogRef: DialogRef, + private webauthnService: WebauthnLoginService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private logService: LogService + ) {} + + ngOnInit(): void { + this.webauthnService + .getCredential$(this.params.credentialId) + .pipe(takeUntil(this.destroy$)) + .subscribe((credential) => (this.credential = credential)); + } + + submit = async () => { + if (this.credential === undefined) { + return; + } + + this.dialogRef.disableClose = true; + try { + await this.webauthnService.deleteCredential(this.credential.id, { + type: VerificationType.MasterPassword, + secret: this.formGroup.value.masterPassword, + }); + this.platformUtilsService.showToast("success", null, this.i18nService.t("passkeyRemoved")); + } catch (error) { + if (error instanceof ErrorResponse && error.statusCode === 400) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("error"), + this.i18nService.t("invalidMasterPassword") + ); + } else { + this.logService.error(error); + this.platformUtilsService.showToast("error", null, this.i18nService.t("unexpectedError")); + } + return false; + } finally { + this.dialogRef.disableClose = false; + } + + this.dialogRef.close(); + }; + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} + +/** + * Strongly typed helper to open a DeleteCredentialDialogComponent + * @param dialogService Instance of the dialog service that will be used to open the dialog + * @param config Configuration for the dialog + */ +export const openDeleteCredentialDialogComponent = ( + dialogService: DialogService, + config: DialogConfig +) => { + return dialogService.open(DeleteCredentialDialogComponent, config); +}; diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/index.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/index.ts new file mode 100644 index 00000000000..87356770952 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/index.ts @@ -0,0 +1 @@ +export * from "./webauthn-login-settings.module"; diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.html b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.html new file mode 100644 index 00000000000..23abe02665c --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.html @@ -0,0 +1,71 @@ +

+ {{ "loginWithPasskey" | i18n }} + + {{ + "on" | i18n + }} + {{ + "off" | i18n + }} + + + + +

+

+ {{ "loginWithPasskeyInfo" | i18n }} + {{ + "learnMoreAboutPasswordless" | i18n + }} +

+ + + + + + + +
{{ credential.name }} + + + {{ "supportsEncryption" | i18n }} + + + {{ "encryptionNotSupported" | i18n }} + + + +
+ +

{{ "passkeyLimitReachedInfo" | i18n }}

+ + + + + + diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.ts new file mode 100644 index 00000000000..98aa517b98e --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.component.ts @@ -0,0 +1,72 @@ +import { Component, HostBinding, OnDestroy, OnInit } from "@angular/core"; +import { Subject, takeUntil } from "rxjs"; + +import { DialogService } from "@bitwarden/components"; + +import { WebauthnLoginService } from "../../core"; +import { WebauthnCredentialView } from "../../core/views/webauth-credential.view"; + +import { openCreateCredentialDialog } from "./create-credential-dialog/create-credential-dialog.component"; +import { openDeleteCredentialDialogComponent } from "./delete-credential-dialog/delete-credential-dialog.component"; + +@Component({ + selector: "app-webauthn-login-settings", + templateUrl: "webauthn-login-settings.component.html", + host: { + "aria-live": "polite", + }, +}) +export class WebauthnLoginSettingsComponent implements OnInit, OnDestroy { + private destroy$ = new Subject(); + + protected readonly MaxCredentialCount = 5; + + protected credentials?: WebauthnCredentialView[]; + protected loading = true; + + constructor( + private webauthnService: WebauthnLoginService, + private dialogService: DialogService + ) {} + + @HostBinding("attr.aria-busy") + get ariaBusy() { + return this.loading ? "true" : "false"; + } + + get hasCredentials() { + return this.credentials && this.credentials.length > 0; + } + + get hasData() { + return this.credentials !== undefined; + } + + get limitReached() { + return this.credentials?.length >= this.MaxCredentialCount; + } + + ngOnInit(): void { + this.webauthnService + .getCredentials$() + .pipe(takeUntil(this.destroy$)) + .subscribe((credentials) => (this.credentials = credentials)); + + this.webauthnService.loading$ + .pipe(takeUntil(this.destroy$)) + .subscribe((loading) => (this.loading = loading)); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + protected createCredential() { + openCreateCredentialDialog(this.dialogService, {}); + } + + protected deleteCredential(credentialId: string) { + openDeleteCredentialDialogComponent(this.dialogService, { data: { credentialId } }); + } +} diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts new file mode 100644 index 00000000000..73e21387ec6 --- /dev/null +++ b/apps/web/src/app/auth/settings/webauthn-login-settings/webauthn-login-settings.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from "@angular/core"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; + +import { SharedModule } from "../../../shared/shared.module"; + +import { CreateCredentialDialogComponent } from "./create-credential-dialog/create-credential-dialog.component"; +import { DeleteCredentialDialogComponent } from "./delete-credential-dialog/delete-credential-dialog.component"; +import { WebauthnLoginSettingsComponent } from "./webauthn-login-settings.component"; + +@NgModule({ + imports: [SharedModule, FormsModule, ReactiveFormsModule], + declarations: [ + WebauthnLoginSettingsComponent, + CreateCredentialDialogComponent, + DeleteCredentialDialogComponent, + ], + exports: [WebauthnLoginSettingsComponent], +}) +export class WebauthnLoginSettingsModule {} diff --git a/apps/web/src/app/oss.module.ts b/apps/web/src/app/oss.module.ts index e1e57302f4b..a68b681dca3 100644 --- a/apps/web/src/app/oss.module.ts +++ b/apps/web/src/app/oss.module.ts @@ -1,6 +1,7 @@ import { NgModule } from "@angular/core"; import { OrganizationUserModule } from "./admin-console/organizations/users/organization-user.module"; +import { AuthModule } from "./auth"; import { LoginModule } from "./auth/login/login.module"; import { TrialInitiationModule } from "./auth/trial-initiation/trial-initiation.module"; import { LooseComponentsModule, SharedModule } from "./shared"; @@ -16,6 +17,7 @@ import { VaultFilterModule } from "./vault/individual-vault/vault-filter/vault-f OrganizationBadgeModule, OrganizationUserModule, LoginModule, + AuthModule, ], exports: [ SharedModule, diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 6efaf3653da..f55cf72551c 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -25,7 +25,6 @@ import { RecoverTwoFactorComponent } from "../auth/recover-two-factor.component" import { RegisterFormModule } from "../auth/register-form/register-form.module"; import { RemovePasswordComponent } from "../auth/remove-password.component"; import { SetPasswordComponent } from "../auth/set-password.component"; -import { ChangePasswordComponent } from "../auth/settings/change-password.component"; import { DeauthorizeSessionsComponent } from "../auth/settings/deauthorize-sessions.component"; import { EmergencyAccessAddEditComponent } from "../auth/settings/emergency-access/emergency-access-add-edit.component"; import { EmergencyAccessAttachmentsComponent } from "../auth/settings/emergency-access/emergency-access-attachments.component"; @@ -119,7 +118,6 @@ import { SharedModule } from "./shared.module"; ApiKeyComponent, AttachmentsComponent, ChangeEmailComponent, - ChangePasswordComponent, CollectionsComponent, DeauthorizeSessionsComponent, DeleteAccountComponent, @@ -204,7 +202,6 @@ import { SharedModule } from "./shared.module"; ApiKeyComponent, AttachmentsComponent, ChangeEmailComponent, - ChangePasswordComponent, CollectionsComponent, DeauthorizeSessionsComponent, DeleteAccountComponent, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 651325bad6f..b9d613877f2 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -611,6 +611,72 @@ "loginWithMasterPassword": { "message": "Log in with master password" }, + "loginWithPasskey": { + "message": "Log in with passkey" + }, + "loginWithPasskeyInfo": { + "message": "Use a generated passkey that will automatically log you in without a password. Biometrics, like facial recognition or fingerprint, or another FIDO2 security method will verify your identity." + }, + "newPasskey": { + "message": "New passkey" + }, + "learnMoreAboutPasswordless": { + "message": "Learn more about passwordless" + }, + "passkeyEnterMasterPassword": { + "message": "Enter your master password to modify log in with passkey settings." + }, + "creatingPasskeyLoading": { + "message": "Creating passkey..." + }, + "creatingPasskeyLoadingInfo": { + "message": "Keep this window open and follow prompts from your browser." + }, + "errorCreatingPasskey": { + "message": "Error creating passkey" + }, + "errorCreatingPasskeyInfo": { + "message": "There was a problem creating your passkey." + }, + "passkeySuccessfullyCreated": { + "message": "Passkey successfully created!" + }, + "customName": { + "message": "Custom name" + }, + "customPasskeyNameInfo": { + "message": "Name your passkey to help you identify it." + }, + "encryptionNotSupported": { + "message": "Encryption not supported" + }, + "loginWithPasskeyEnabled": { + "message": "Log in with passkey turned on" + }, + "passkeySaved": { + "message": "$NAME$ saved", + "placeholders": { + "name": { + "content": "$1", + "example": "Personal yubikey" + } + } + }, + "passkeyRemoved": { + "message": "Passkey removed" + }, + "removePasskey": { + "message": "Remove passkey" + }, + "removePasskeyInfo": { + "message": "If all passkeys are removed, you will be unable to log into new devices without your master password." + }, + "passkeyLimitReachedInfo": { + "message": "Passkey limit reached. Remove a passkey to add another." + }, + "tryAgain": { + "message": "Try again" + }, "createAccount": { "message": "Create account" }, @@ -5406,6 +5472,19 @@ "required": { "message": "required" }, + "charactersCurrentAndMaximum": { + "message": "$CURRENT$/$MAX$ character maximum", + "placeholders": { + "current": { + "content": "$1", + "example": "0" + }, + "max": { + "content": "$2", + "example": "100" + } + } + }, "characterMaximum": { "message": "$MAX$ character maximum", "placeholders": { @@ -5754,6 +5833,9 @@ "on": { "message": "On" }, + "off": { + "message": "Off" + }, "members": { "message": "Members" }, diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 9ca25ab2879..cc0873351b8 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -2,6 +2,7 @@ export enum FeatureFlag { DisplayEuEnvironmentFlag = "display-eu-environment", DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning", TrustedDeviceEncryption = "trusted-device-encryption", + PasswordlessLogin = "passwordless-login", AutofillV2 = "autofill-v2", BrowserFilelessImport = "browser-fileless-import", } diff --git a/libs/components/src/dialog/directives/dialog-close.directive.ts b/libs/components/src/dialog/directives/dialog-close.directive.ts index 543c37715dd..5e44ced7c21 100644 --- a/libs/components/src/dialog/directives/dialog-close.directive.ts +++ b/libs/components/src/dialog/directives/dialog-close.directive.ts @@ -1,5 +1,5 @@ import { DialogRef } from "@angular/cdk/dialog"; -import { Directive, HostListener, Input, Optional } from "@angular/core"; +import { Directive, HostBinding, HostListener, Input, Optional } from "@angular/core"; @Directive({ selector: "[bitDialogClose]", @@ -9,7 +9,17 @@ export class DialogCloseDirective { constructor(@Optional() public dialogRef: DialogRef) {} - @HostListener("click") close(): void { + @HostBinding("attr.disabled") + get disableClose() { + return this.dialogRef?.disableClose ? true : null; + } + + @HostListener("click") + close(): void { + if (this.disableClose) { + return; + } + this.dialogRef.close(this.dialogResult); } }