mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-22 07:50:04 +02:00
Merge branch 'main' into tools/PM-8280/email-forwarders
This commit is contained in:
commit
1ad1b900ea
@ -293,6 +293,12 @@
|
||||
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/send-ui/*", "src/**/*"] }]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["libs/tools/card/src/**/*.ts"],
|
||||
"rules": {
|
||||
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/tools-card/*", "src/**/*"] }]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["libs/vault/src/**/*.ts"],
|
||||
"rules": {
|
||||
|
1
.github/whitelist-capital-letters.txt
vendored
1
.github/whitelist-capital-letters.txt
vendored
@ -24,6 +24,7 @@
|
||||
./libs/tools/README.md
|
||||
./libs/tools/export/vault-export/README.md
|
||||
./libs/tools/send/README.md
|
||||
./libs/tools/card/README.md
|
||||
./libs/vault/README.md
|
||||
./README.md
|
||||
./LICENSE_BITWARDEN.txt
|
||||
|
@ -19,6 +19,8 @@ const config: StorybookConfig = {
|
||||
"../apps/browser/src/**/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../bitwarden_license/bit-web/src/**/*.mdx",
|
||||
"../bitwarden_license/bit-web/src/**/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../libs/tools/card/src/**/*.mdx",
|
||||
"../libs/tools/card/src/**/*.stories.@(js|jsx|ts|tsx)",
|
||||
],
|
||||
addons: [
|
||||
getAbsolutePath("@storybook/addon-links"),
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "المولّد",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "قم بإنشاء كلمات مرور قوية وفريدة لتسجيلات الدخول الخاصة بك."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "تأكيد"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Yaradıcı",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Hesablarınız üçün avtomatik olaraq güclü, unikal parollar yaradın."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send uğurla yaradıldı!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Send, növbəti $DAYS$ ərzində keçidə sahib olan hər kəsə əlçatan olacaq.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send, növbəti 1 saat ərzində keçidə sahib olan hər kəsə əlçatan olacaq.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send, növbəti $HOURS$ saat ərzində keçidə sahib olan hər kəsə əlçatan olacaq.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send, növbəti 1 gün ərzində keçidə sahib olan hər kəsə əlçatan olacaq.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send, növbəti $DAYS$ gün ərzində keçidə sahib olan hər kəsə əlçatan olacaq.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Bu sayt üçün uyuşan giriş məlumatı yoxdur"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Axtarın və ya keçid açarını yeni bir giriş olaraq saxlayın"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Təsdiqlə"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Генератар",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Аўтаматычна генерыруйце надзейныя і ўнікальныя паролі для вашых лагінаў."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Пацвердзіць"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Генератор",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Автоматично създаване на силни и неповторими пароли."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Изпращането е създадено успешно!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Изпращането ще бъде достъпно за всеки с връзката през следващия 1 час.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Изпращането ще бъде достъпно за всеки с връзката през следващите $HOURS$ часа.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Изпращането ще бъде достъпно за всеки с връзката през следващия 1 ден.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Изпращането ще бъде достъпно за всеки с връзката през следващите $DAYS$ дни.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Няма записи за вписване отговарящи на този уеб сайт"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Търсене или запазване на секретния ключ като нов елемент за вписване"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Потвърждаване"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "উৎপাদক",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "আপনার লগইনগুলির জন্য স্বয়ংক্রিয়ভাবে শক্তিশালী, অদ্বিতীয় পাসওয়ার্ড তৈরি করুন।"
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generador",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Genera automàticament contrasenyes fortes i úniques per als vostres inicis de sessió."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirma-ho"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generátor",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Vygenerujte si silné a unikátní heslo pro přihlašovací údaje."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send byl úspěšně vytvořen!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send bude k dispozici na příští 1 hodinu každému, kdo má odkaz.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send bude k dispozici na příštích $HOURS$ hodin každému, kdo má odkaz.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send bude k dispozici na příští 1 den každému, kdo má odkaz.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send bude k dispozici na příštích $DAYS$ dnů každému, kdo má odkaz.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Žádné odpovídající přihlašovací údaje pro tento web"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Vyhledat nebo uložit přístupový klíč jako nové přihlášení"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Potvrdit"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Cynhyrchydd",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Cynhyrchu cyfrineiriau cryf ac unigryw ar gyfer eich cyfrifon yn awtomatig."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Cadarnhau"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Opret automatisk stærke, unikke adgangskoder til dine logins."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send er hermed oprettet!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Denne Send vil være tilgængelig for alle med linket i de næste $DAYS$ dage.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Denne Send vil være tilgængelig for alle med linket den kommende time.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Denne Send vil være tilgængelig for alle med linket de kommende $HOURS$ timer.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Denne Send vil være tilgængelig for alle med linket den kommende dag.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Denne Send vil være tilgængelig for alle med linket de kommende $DAYS$ dage.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Ingen matchende logins for dette websted"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Søg eller gem adgangsnøgle som nyt login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Bekræft"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Generiert automatisch ein starkes und einzigartiges Passwort."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send erfolgreich erstellt!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Das Send wird jedem mit dem Link für die nächste Stunde zur Verfügung stehen.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Das Send wird jedem mit dem Link für die nächsten $HOURS$ Stunden zur Verfügung stehen.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Das Send wird jedem mit dem Link für den nächsten Tag zur Verfügung stehen.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Das Send wird jedem mit dem Link für die nächsten $DAYS$ Tage zur Verfügung stehen.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Keine passenden Zugangsdaten für diese Seite"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Passkeys suchen oder als neue Zugangsdaten speichern"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Bestätigen"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Γεννήτρια",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Δημιουργήστε αυτόματα ισχυρούς και μοναδικούς κωδικούς πρόσβασης για τις συνδέσεις σας."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Το Send δημιουργήθηκε επιτυχώς!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Το Send θα είναι διαθέσιμο σε όποιον έχει τον σύνδεσμο για την επόμενη 1 ώρα.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Το Send θα είναι διαθέσιμο σε όποιον έχει τον σύνδεσμο για τις επόμενες $HOURS$ ώρες.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Το Send θα είναι διαθέσιμο σε όποιον έχει τον σύνδεσμο για την επόμενη 1 ημέρα.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Το Send θα είναι διαθέσιμο σε όποιον έχει τον σύνδεσμο για τις επόμενες $DAYS$ ημέρες.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Δεν υπάρχουν αντίστοιχες συνδέσεις για αυτόν τον ιστότοπο"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Αναζήτηση ή αποθήκευση κωδικού πρόσβασης ως νέα σύνδεση"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Επιβεβαίωση"
|
||||
},
|
||||
|
@ -1408,6 +1408,12 @@
|
||||
"showInlineMenuLabel": {
|
||||
"message": "Show autofill suggestions on form fields"
|
||||
},
|
||||
"showInlineMenuIdentitiesLabel": {
|
||||
"message": "Display identities as suggestions"
|
||||
},
|
||||
"showInlineMenuCardsLabel": {
|
||||
"message": "Display cards as suggestions"
|
||||
},
|
||||
"showInlineMenuOnIconSelectionLabel": {
|
||||
"message": "Display suggestions when icon is selected"
|
||||
},
|
||||
@ -2498,7 +2504,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -2629,6 +2653,15 @@
|
||||
"message": "Your organization requires you to set a master password.",
|
||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||
},
|
||||
"cardMetrics": {
|
||||
"message": "out of $TOTAL$",
|
||||
"placeholders": {
|
||||
"total": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"verificationRequired": {
|
||||
"message": "Verification required",
|
||||
"description": "Default title for the user verification dialog."
|
||||
@ -3662,6 +3695,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generador",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Genera automáticamente contraseñas fuertes y únicas para tus accesos."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirmar"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Genereerija",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Loo oma kontodele tugevaid ja unikaalseid paroole."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Sortzailea",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatikoki pasahitz sendo eta bakarrak sortzen ditu zure saio-hasieratarako."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Berretsi"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "تولید کننده",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "به طور خودکار کلمههای عبور قوی و منحصر به فرد برای ورود به سیستم خود ایجاد کنید."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "تأیید"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generaattori",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Luo kirjautumistiedoillesi automaattisesti vahvoja, ainutlaatuisia salasanoja."
|
||||
@ -605,13 +605,13 @@
|
||||
"message": "Holvisi on lukittu. Jatka vahvistamalla henkilöllisyytesi."
|
||||
},
|
||||
"yourVaultIsLockedV2": {
|
||||
"message": "Your vault is locked"
|
||||
"message": "Holvisi on lukittu"
|
||||
},
|
||||
"yourAccountIsLocked": {
|
||||
"message": "Your account is locked"
|
||||
"message": "Tilisi on lukittu"
|
||||
},
|
||||
"or": {
|
||||
"message": "or"
|
||||
"message": "tai"
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Avaa"
|
||||
@ -1946,7 +1946,7 @@
|
||||
"message": "Avaa biometrialla"
|
||||
},
|
||||
"unlockWithMasterPassword": {
|
||||
"message": "Unlock with master password"
|
||||
"message": "Avaa pääsalasanalla"
|
||||
},
|
||||
"awaitDesktop": {
|
||||
"message": "Odottaa vahvistusta työpöydältä"
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Sendin luonti onnistui!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Tämän linkin välityksellä Send on kenen tahansa avattavissa seuraavien $DAYS$ päivän ajan.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3636,7 +3654,7 @@
|
||||
"message": "Pääsyavain"
|
||||
},
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
"message": "Avataan"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "Pääsyavainta ei kopioida"
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Tälle sivustolle sopivia kirjautumistietoja ei ole"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Vahvista"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Magmamana",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatiko na gumawa ng mga malakas at natatanging mga password para sa iyong mga logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Générateur",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Générer automatiquement des mots de passe robustes et uniques pour vos identifiants."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send créé avec succès !",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Le Send est disponible à toute personne ayant le lien durant les $DAYS$ prochains jours.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Le Send sera accessible à toute personne disposant du lien pendant une heure.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Le Send sera accessible à toute personne disposant du lien pendant les prochaines $HOURS$ heures.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Le Send sera accessible à toute personne disposant du lien pendant un jour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Le Send sera accessible à toute personne disposant du lien pendant les prochains $DAYS$ jours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Aucun identifiant correspondant pour ce site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Rechercher ou enregistrer la clé d'accès comme nouvel identifiant"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirmer"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Xerador",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Xera automaticamente contrasinais fortes e únicos para os seus inicios de sesión."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "מייצר",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "צור אוטומטית סיסמאות חזקות ויחודיות עבור פרטי הכניסה שלך."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "उत्पन्न करें!",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "स्वचालित रूप से अपने लॉगिन के लिए मजबूत, अद्वितीय पासवर्ड उत्पन्न करते हैं।"
|
||||
@ -605,13 +605,13 @@
|
||||
"message": "आपकी वॉल्ट लॉक हो गई है। जारी रखने के लिए अपने मास्टर पासवर्ड को सत्यापित करें।"
|
||||
},
|
||||
"yourVaultIsLockedV2": {
|
||||
"message": "Your vault is locked"
|
||||
"message": "तिजोरी पर लॉक लगा है"
|
||||
},
|
||||
"yourAccountIsLocked": {
|
||||
"message": "Your account is locked"
|
||||
"message": "आपका खाता बंद है."
|
||||
},
|
||||
"or": {
|
||||
"message": "or"
|
||||
"message": "या"
|
||||
},
|
||||
"unlock": {
|
||||
"message": "ताला खोलें"
|
||||
@ -1946,7 +1946,7 @@
|
||||
"message": "बायोमेट्रिक्स का उपयोग कर अनलॉक करें"
|
||||
},
|
||||
"unlockWithMasterPassword": {
|
||||
"message": "Unlock with master password"
|
||||
"message": "मास्टर पासवर्ड से खोलें"
|
||||
},
|
||||
"awaitDesktop": {
|
||||
"message": "डेस्कटॉप से पुष्टि का इंतजार"
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3636,7 +3654,7 @@
|
||||
"message": "Passkey"
|
||||
},
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
"message": "ऐक्सेसिंग"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "Passkey will not be copied"
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatski generiraj jake, jedinstvene lozinke."
|
||||
@ -490,7 +490,7 @@
|
||||
"description": "Label for the avoid ambiguous characters checkbox."
|
||||
},
|
||||
"generatorPolicyInEffect": {
|
||||
"message": "Enterprise policy requirements have been applied to your generator options.",
|
||||
"message": "Pravila tvrtke primjenjena su na generator.",
|
||||
"description": "Indicates that a policy limits the credential generator screen."
|
||||
},
|
||||
"searchVault": {
|
||||
@ -536,7 +536,7 @@
|
||||
"message": "Bilješke"
|
||||
},
|
||||
"privateNote": {
|
||||
"message": "Private note"
|
||||
"message": "Privatna bilješka"
|
||||
},
|
||||
"note": {
|
||||
"message": "Bilješka"
|
||||
@ -605,13 +605,13 @@
|
||||
"message": "Tvoj trezor je zaključan. Potvrdi glavnu lozinku za nastavak."
|
||||
},
|
||||
"yourVaultIsLockedV2": {
|
||||
"message": "Your vault is locked"
|
||||
"message": "Trezor je zaključan"
|
||||
},
|
||||
"yourAccountIsLocked": {
|
||||
"message": "Your account is locked"
|
||||
"message": "Račun je zaključan"
|
||||
},
|
||||
"or": {
|
||||
"message": "or"
|
||||
"message": "ili"
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Otključaj"
|
||||
@ -1153,7 +1153,7 @@
|
||||
"message": "Datoteka"
|
||||
},
|
||||
"fileToShare": {
|
||||
"message": "File to share"
|
||||
"message": "Datoteka za dijeljenje"
|
||||
},
|
||||
"selectFile": {
|
||||
"message": "Odaberi datoteku."
|
||||
@ -1946,7 +1946,7 @@
|
||||
"message": "Otključaj biometrijom"
|
||||
},
|
||||
"unlockWithMasterPassword": {
|
||||
"message": "Unlock with master password"
|
||||
"message": "Otključaj glavnom lozinkom"
|
||||
},
|
||||
"awaitDesktop": {
|
||||
"message": "Čekanje potvrde iz desktop aplikacije"
|
||||
@ -2287,14 +2287,14 @@
|
||||
"message": "Spremljene promjene izuzete domene"
|
||||
},
|
||||
"limitSendViews": {
|
||||
"message": "Limit views"
|
||||
"message": "Ograniči broj pogleda"
|
||||
},
|
||||
"limitSendViewsHint": {
|
||||
"message": "No one can view this Send after the limit is reached.",
|
||||
"message": "Nakon dosegnutog broja, nitko neće moći pogledati Send",
|
||||
"description": "Displayed under the limit views field on Send"
|
||||
},
|
||||
"limitSendViewsCount": {
|
||||
"message": "$ACCESSCOUNT$ views left",
|
||||
"message": "Preostalo pogleda: $ACCESSCOUNT$",
|
||||
"description": "Displayed under the limit views field on Send",
|
||||
"placeholders": {
|
||||
"accessCount": {
|
||||
@ -2387,7 +2387,7 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"deleteSendPermanentConfirmation": {
|
||||
"message": "Are you sure you want to permanently delete this Send?",
|
||||
"message": "Sigurno želiš trajno izbrisati ovaj Send?",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"editSend": {
|
||||
@ -2450,7 +2450,7 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendPasswordDescV2": {
|
||||
"message": "Require this password to view the Send.",
|
||||
"message": "Zahtijevaj lozinku za pregled Senda.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendNotesDesc": {
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send je uspješno stvoren!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Send će biti dostupan svakome s poveznicom ovoliko dana: $DAYS$.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send će biti dostupan svakome s poveznicom sljedećih sat vremena.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send če biti dostupan svakome s poveznicom ovoliko sati: $HOURS$",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send će biti dostupan svakome s poveznicom 1 dan.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send će biti dostupan svakome s poveznom ovoliko dana: $DAYS$",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -2517,11 +2535,11 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendFilePopoutDialogText": {
|
||||
"message": "Pop out extension?",
|
||||
"message": "Otvori proširenje?",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendFilePopoutDialogDesc": {
|
||||
"message": "To create a file Send, you need to pop out the extension to a new window.",
|
||||
"message": "Za stvaranje Senda, potrebno je otvoriti proširenje u novi prozor.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendLinuxChromiumFileWarning": {
|
||||
@ -2534,7 +2552,7 @@
|
||||
"message": "Za odabir datoteke u Safariju, otvori iskočni prozor klikom na ovu poruku."
|
||||
},
|
||||
"popOut": {
|
||||
"message": "Pop out"
|
||||
"message": "Otvori"
|
||||
},
|
||||
"sendFileCalloutHeader": {
|
||||
"message": "Prije početka"
|
||||
@ -2570,7 +2588,7 @@
|
||||
"message": "Sakrij moju adresu e-pošte od primatelja."
|
||||
},
|
||||
"hideYourEmail": {
|
||||
"message": "Hide your email address from viewers."
|
||||
"message": "Autentifikacija"
|
||||
},
|
||||
"sendOptionsPolicyInEffect": {
|
||||
"message": "Jedno ili više pravila organizacije utječe na postavke Senda."
|
||||
@ -3636,7 +3654,7 @@
|
||||
"message": "Pristupni ključ"
|
||||
},
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
"message": "Pristupanje"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "Pristupni ključ neće biti kopiran"
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Nema prijava za ovu web stranicu"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Potraži ili spremi pristupni ključ kao novu prijavu"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Autoriziraj"
|
||||
},
|
||||
@ -4232,7 +4253,7 @@
|
||||
"message": "Omogući animacije"
|
||||
},
|
||||
"showAnimations": {
|
||||
"message": "Show animations"
|
||||
"message": "Prikaži animacije"
|
||||
},
|
||||
"addAccount": {
|
||||
"message": "Dodaj račun"
|
||||
@ -4513,6 +4534,6 @@
|
||||
"message": "Nemaš prava za uređivanje ove stavke"
|
||||
},
|
||||
"authenticating": {
|
||||
"message": "Authenticating"
|
||||
"message": "Autentifikacija"
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generátor",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatikusan létrehoz erős, egyedi jelszavakat a bejelentkezéseidhez."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "A Send sikeresen létrejött!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "A Send bárki számára elérhető a hivatkozással a következő 1 órában.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "A Send bárki számára elérhető a hivatkozással a következő $HOURS$ órában.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "A Send bárki számára elérhető a hivatkozással a következő 1 napban.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "A Send bárki számára elérhető a hivatkozással a következő $DAYS$ napban.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Nincsenek egyező bejelentkezések ehhez a webhelyhez."
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Keressés vagy a belépőkulcs mentése bejelentkezésként."
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Megerősítés"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Pembuat Sandi",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Secara otomatis membuat sandi yang kuat dan unik untuk info masuk Anda."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generatore",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Genera automaticamente password complesse e uniche per i tuoi login."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send creato con successo!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Il Send sarà disponibile a qualsiasi utente con il link per i prossimi $DAYS$ giorni.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Nessun login corrispondente per questo sito"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Conferma"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "パス生成",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "ログインのために強固なユニークパスワードを自動的に生成します。"
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send を作成しました!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Send は、次の $DAYS$ 日間はリンクを知っている人全員が利用できます。",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send は、次の1時間はリンクを知っている全員が利用できます。",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send は、次の$HOURS$時間はリンクを知っている人全員が利用できます。",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send は、次の1日間はリンクを知っている全員が利用できます。",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send は、次の$DAYS$日間はリンクを知っている人全員が利用できます。",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "このサイトに一致するログイン情報がありません"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "パスキーを検索または新しいログインとして保存"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "確認"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "ಜನರೇಟರ್",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "ನಿಮ್ಮ ಲಾಗಿನ್ಗಳಿಗಾಗಿ ಬಲವಾದ, ಅನನ್ಯ ಪಾಸ್ವರ್ಡ್ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ರಚಿಸಿ."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "생성기",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "유일무이하고 강력한 비밀번호를 자동으로 생성합니다."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generatorius",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatiškai generuokite stiprius, unikalius prisijungimo slaptažodžius."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Veidotājs",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automātiski veido spēcīgas, neatkārtojamas paroles visiem pieteikšanās vienumiem."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send tika veiksmīgi izveidots.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send būs pieejams nākamo stundu ikvienam, kuram ir saite.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send būs pieejams nākamās $HOURS$ stundas ikvienam, kuram ir saite.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send būs pieejams nākamo dienu ikvienam, kuram ir saite.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send būs pieejams nākamās $DAYS$ dienas ikvienam, kuram ir saite.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Šai vietnei nav atbilstošu pieteikšanās vietnumu"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Meklēt vai saglabāt piekļuves atslēgu kā jaunu pieteikšanās vienumu"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Apstiprināt"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "സ്രഷ്ടാവ്",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "യാന്ത്രികമായി ശക്തമായ പാസ്സ്വേർഡുകൾ നിങ്ങളുടെ ലോഗിന് വേണ്ടി നിർമിക്കുക "
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "जनित्र",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Generer automatisk sterke og unike passord for dine innlogginger."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Bekreft"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatisch sterke, unieke wachtwoorden voor je logins genereren."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send succesvol aangemaakt!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "De Send is het komende uur beschikbaar voor iedereen met de link.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "De Send is de komende $HOURS$ uur beschikbaar voor iedereen met de link.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "De Send is de komende dag beschikbaar voor iedereen met de link.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "De Send is de komende $DAYS$ dagen beschikbaar voor iedereen met de link.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Geen overeenkomende logins voor deze site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Passkey zoeken of als nieuwe login opslaan"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Bevestigen"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatycznie wygeneruj silne, unikatowe hasła dla swoich loginów."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send został stworzony!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Send będzie dostępny dla każdego kto ma link przez następującą liczbę dni: $DAYS$.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Brak pasujących loginów dla tej witryny"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Potwierdź"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Gerador",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Gere automaticamente senhas fortes e únicas para as suas credenciais."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Envio criado com sucesso!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "O envio estará disponível para qualquer pessoa com o link para os próximos $DAYS$ dias.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Sem credenciais correspondentes para este site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirmar"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Gerador",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Gera automaticamente palavras-passe fortes e únicas para as suas credenciais."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send criado com sucesso!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "O Send estará disponível para qualquer pessoa que tenha o link durante a próxima hora.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "O Send estará disponível para qualquer pessoa que tenha o link durante as próximas $HOURS$.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "O Send estará disponível para qualquer pessoa que tenha o link durante o próximo dia.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "O Send estará disponível para qualquer pessoa que tenha o link durante os próximos $DAYS$ dias.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Sem credenciais correspondentes para este site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Procure ou guarde a chave de acesso como uma nova credencial"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirmar"
|
||||
},
|
||||
@ -3669,7 +3690,7 @@
|
||||
"message": "Guardar a chave de acesso"
|
||||
},
|
||||
"savePasskeyNewLogin": {
|
||||
"message": "Guardar a chave de acesso como uma nova credencial"
|
||||
"message": "Guarde a chave de acesso como uma nova credencial"
|
||||
},
|
||||
"chooseCipherForPasskeySave": {
|
||||
"message": "Escolha uma credencial para guardar esta chave de acesso"
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Generează automat parole unice și puternice pentru autentificările dvs."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Генератор",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Автоматическая генерация сильных и уникальных паролей для ваших логинов."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send успешно создана!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Send будет доступна всем, кто получит ссылку в течение следующих дней: $DAYS$.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send будет доступна всем, кто получит ссылку в течение следующего 1 часа.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send будет доступна всем, кто получит ссылку в течение следующих $HOURS$ часов.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send будет доступна всем, кто получит ссылку в течение следующего 1 дня.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send будет доступна всем, кто получит ссылку в течение следующих $DAYS$ дней.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Нет подходящих логинов для этого сайта"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Найти или сохранить passkey как новый логин"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Подтвердить"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "උත්පාදක යන්ත්රය",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "ඔබේ පිවිසුම් සඳහා ශක්තිමත්, අද්විතීය මුරපද ස්වයංක්රීයව ජනනය කරන්න."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generátor",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automaticky generovať silné a unikátne heslá k prihlasovacím údajom."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send bol úspešne vytvorený!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send bude k dispozícii každému s odkazom nasledujúcu hodinu.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Send bude k dispozícii každému s odkazom po dobu $HOURS$ hod.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send bude k dispozícii každému s odkazom nasledujúci deň.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Send bude k dispozícii každému s odkazom po dobu $DAYS$ dní.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Pre túto stránku sa nenašli prihlasovacie údaje"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Vyhľadať alebo uložiť prístupový kľúč ako nové prihlasovacie údaje"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Potvrdiť"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Avtomatično generiraj močna, edinstvena gesla za vaše prijave."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Генератор",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Аутоматски генеришите јаке, јединствене лозинке за ваше пријаве."
|
||||
@ -605,13 +605,13 @@
|
||||
"message": "Сеф је закључан. Унесите главну лозинку за наставак."
|
||||
},
|
||||
"yourVaultIsLockedV2": {
|
||||
"message": "Your vault is locked"
|
||||
"message": "Ваш сеф је блокиран"
|
||||
},
|
||||
"yourAccountIsLocked": {
|
||||
"message": "Your account is locked"
|
||||
"message": "Ваш налог је закључан"
|
||||
},
|
||||
"or": {
|
||||
"message": "or"
|
||||
"message": "или"
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Откључај"
|
||||
@ -1946,7 +1946,7 @@
|
||||
"message": "Откључавајте помоћу биометрије"
|
||||
},
|
||||
"unlockWithMasterPassword": {
|
||||
"message": "Unlock with master password"
|
||||
"message": "Откључај са главном лозинком"
|
||||
},
|
||||
"awaitDesktop": {
|
||||
"message": "Чекање потврде са десктопа"
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send је успешно направљен!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Send ће бити доступно свима који имају везу наредних 1 сат.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Send ће бити доступно свима који имају везу наредних 1 дан.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3636,7 +3654,7 @@
|
||||
"message": "Приступачни кључ"
|
||||
},
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
"message": "Приступ"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "Приступачни кључ неће бити копиран"
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Нема одговарајућих пријава за овај сајт"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Претражити или сачувати приступни кључ као нову пријаву"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Потврди"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Skapa starka och unika lösenord automatiskt för dina inloggningar."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Bekräfta"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Automatically generate strong, unique passwords for your logins."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "สุ่มรหัส",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำใครโดยอัตโนมัติสำหรับการเข้าสู่ระบบของคุณ"
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Confirm"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Oluşturucu",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Hesaplarınız için otomatik olarak güçlü, özgün parolalar oluşturun."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send başarıyla oluşturuldu.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "$DAYS$ gün boyunca bu bağlantıya sahip olan herkes bu Send'e ulaşabilir.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "Önümüzdeki 1 saat boyunca bu bağlantıya sahip olan herkes bu Send'e ulaşabilir.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "Önümüzdeki $HOURS$ saat boyunca bu bağlantıya sahip olan herkes bu Send'e ulaşabilir.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "Önümüzdeki 1 gün boyunca bu bağlantıya sahip olan herkes bu Send'e ulaşabilir.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "Önümüzdeki $DAYS$ gün boyunca bu bağlantıya sahip olan herkes bu Send'e ulaşabilir.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Bu siteyle eşleşen hesap bulunamadı"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Onayla"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Генератор",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Автоматичне генерування стійких, унікальних паролів."
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Відправлення успішно створено!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "Це відправлення буде доступним будь-кому за посиланням протягом $DAYS$ днів.",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "Немає відповідних записів для цього сайту"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Підтвердити"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "Trình tạo",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "Tự động tạo mật khẩu mạnh mẽ, độc nhất cho đăng nhập của bạn."
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Xác nhận"
|
||||
},
|
||||
|
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "生成器",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "自动生成安全可靠唯一的登录密码。"
|
||||
@ -2498,8 +2498,26 @@
|
||||
"message": "Send 创建成功!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"message": "在接下来的 $DAYS$ 天内,任何拥有链接的人都可以访问此 Send。",
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@ -3636,7 +3654,7 @@
|
||||
"message": "通行密钥"
|
||||
},
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
"message": "正在访问"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "通行密钥不会被复制"
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "此站点没有匹配的登录项目"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "搜索或将通行密钥保存为一个新的登录"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "确认"
|
||||
},
|
||||
|
@ -195,7 +195,7 @@
|
||||
"message": "新增項目"
|
||||
},
|
||||
"accountEmail": {
|
||||
"message": "帳號電子郵件"
|
||||
"message": "帳戶電子郵件"
|
||||
},
|
||||
"requestHint": {
|
||||
"message": "請求提示"
|
||||
@ -329,10 +329,10 @@
|
||||
"message": "編輯資料夾"
|
||||
},
|
||||
"newFolder": {
|
||||
"message": "New folder"
|
||||
"message": "新增資料夾"
|
||||
},
|
||||
"folderName": {
|
||||
"message": "Folder name"
|
||||
"message": "資料夾名稱"
|
||||
},
|
||||
"folderHintText": {
|
||||
"message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums"
|
||||
@ -381,7 +381,7 @@
|
||||
},
|
||||
"generator": {
|
||||
"message": "產生器",
|
||||
"description": "Short for 'Password Generator'."
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "自動產生安全、唯一的登入密碼。"
|
||||
@ -557,7 +557,7 @@
|
||||
"message": "前往"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "Launch website"
|
||||
"message": "開啟網站"
|
||||
},
|
||||
"website": {
|
||||
"message": "網站"
|
||||
@ -572,7 +572,7 @@
|
||||
"message": "其他"
|
||||
},
|
||||
"unlockMethods": {
|
||||
"message": "Unlock options"
|
||||
"message": "解鎖選項"
|
||||
},
|
||||
"unlockMethodNeededToChangeTimeoutActionDesc": {
|
||||
"message": "設定解鎖方法來變更您的密碼庫逾時動作。"
|
||||
@ -581,13 +581,13 @@
|
||||
"message": "在設定中設定一個解鎖方式"
|
||||
},
|
||||
"sessionTimeoutHeader": {
|
||||
"message": "Session timeout"
|
||||
"message": "工作階段逾時"
|
||||
},
|
||||
"vaultTimeoutHeader": {
|
||||
"message": "Vault timeout"
|
||||
"message": "密碼庫逾時時間"
|
||||
},
|
||||
"otherOptions": {
|
||||
"message": "Other options"
|
||||
"message": "其它選項"
|
||||
},
|
||||
"rateExtension": {
|
||||
"message": "為本套件評分"
|
||||
@ -608,10 +608,10 @@
|
||||
"message": "Your vault is locked"
|
||||
},
|
||||
"yourAccountIsLocked": {
|
||||
"message": "Your account is locked"
|
||||
"message": "您的帳戶已被鎖定。"
|
||||
},
|
||||
"or": {
|
||||
"message": "or"
|
||||
"message": "或"
|
||||
},
|
||||
"unlock": {
|
||||
"message": "解鎖"
|
||||
@ -636,7 +636,7 @@
|
||||
"message": "密碼庫逾時時間"
|
||||
},
|
||||
"vaultTimeout1": {
|
||||
"message": "Timeout"
|
||||
"message": "逾時"
|
||||
},
|
||||
"lockNow": {
|
||||
"message": "立即鎖定"
|
||||
@ -929,7 +929,7 @@
|
||||
"message": "詢問新增登入資料"
|
||||
},
|
||||
"vaultSaveOptionsTitle": {
|
||||
"message": "Save to vault options"
|
||||
"message": "儲存至密碼庫選項"
|
||||
},
|
||||
"addLoginNotificationDesc": {
|
||||
"message": "在密碼庫中找不到相符的項目時詢問是否新增項目。"
|
||||
@ -997,7 +997,7 @@
|
||||
"message": "解鎖"
|
||||
},
|
||||
"additionalOptions": {
|
||||
"message": "Additional options"
|
||||
"message": "額外選項"
|
||||
},
|
||||
"enableContextMenuItem": {
|
||||
"message": "顯示內容選單選項"
|
||||
@ -1033,7 +1033,7 @@
|
||||
"description": "Light color"
|
||||
},
|
||||
"solarizedDark": {
|
||||
"message": "Solarized Dark 主題",
|
||||
"message": "Solarized 深色主題",
|
||||
"description": "'Solarized' is a noun and the name of a color scheme. It should not be translated."
|
||||
},
|
||||
"exportFrom": {
|
||||
@ -1061,7 +1061,7 @@
|
||||
"message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption."
|
||||
},
|
||||
"exportTypeHeading": {
|
||||
"message": "Export type"
|
||||
"message": "匯出類型"
|
||||
},
|
||||
"accountRestricted": {
|
||||
"message": "Account restricted"
|
||||
@ -1527,7 +1527,7 @@
|
||||
"message": "布林值"
|
||||
},
|
||||
"cfTypeCheckbox": {
|
||||
"message": "Checkbox"
|
||||
"message": "核取方塊"
|
||||
},
|
||||
"cfTypeLinked": {
|
||||
"message": "連結型",
|
||||
@ -1712,7 +1712,7 @@
|
||||
"message": "身分"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"message": "新增 $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
@ -1721,7 +1721,7 @@
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"message": "編輯 $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
@ -1730,7 +1730,7 @@
|
||||
}
|
||||
},
|
||||
"viewItemHeader": {
|
||||
"message": "View $TYPE$",
|
||||
"message": "檢視 $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
@ -1748,7 +1748,7 @@
|
||||
"message": "集合"
|
||||
},
|
||||
"nCollections": {
|
||||
"message": "$COUNT$ collections",
|
||||
"message": "$COUNT$ 個集合",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
@ -1801,7 +1801,7 @@
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"baseDomainOptionRecommended": {
|
||||
"message": "Base domain (recommended)",
|
||||
"message": "基礎網域 (推薦)",
|
||||
"description": "Domain name. Ex. website.com"
|
||||
},
|
||||
"domainName": {
|
||||
@ -1855,7 +1855,7 @@
|
||||
"message": "沒有可列出的密碼。"
|
||||
},
|
||||
"clearHistory": {
|
||||
"message": "Clear history"
|
||||
"message": "清除歷史紀錄"
|
||||
},
|
||||
"noPasswordsToShow": {
|
||||
"message": "No passwords to show"
|
||||
@ -1922,10 +1922,10 @@
|
||||
"message": "使用 PIN 碼解鎖"
|
||||
},
|
||||
"setYourPinTitle": {
|
||||
"message": "Set PIN"
|
||||
"message": "設定 PIN 碼"
|
||||
},
|
||||
"setYourPinButton": {
|
||||
"message": "Set PIN"
|
||||
"message": "設定 PIN 碼"
|
||||
},
|
||||
"setYourPinCode": {
|
||||
"message": "設定您用來解鎖 Bitwarden 的 PIN 碼。您的 PIN 設定將在您完全登出本應用程式時被重設。"
|
||||
@ -1999,7 +1999,7 @@
|
||||
"message": "密碼庫逾時動作"
|
||||
},
|
||||
"vaultTimeoutAction1": {
|
||||
"message": "Timeout action"
|
||||
"message": "逾時後動作"
|
||||
},
|
||||
"lock": {
|
||||
"message": "鎖定",
|
||||
@ -2498,7 +2498,25 @@
|
||||
"message": "Send created successfully!",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendAvailability": {
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
@ -3662,6 +3680,9 @@
|
||||
"noMatchingLoginsForSite": {
|
||||
"message": "No matching logins for this site"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "確認"
|
||||
},
|
||||
@ -3913,7 +3934,7 @@
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"message": "複製資訊 - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3933,7 +3954,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsLabel": {
|
||||
"message": "More options, $ITEMNAME$",
|
||||
"message": "更多選項, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3943,7 +3964,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsTitle": {
|
||||
"message": "More options - $ITEMNAME$",
|
||||
"message": "更多選項 - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3976,7 +3997,7 @@
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignToCollections": {
|
||||
"message": "Assign to collections"
|
||||
"message": "指派至集合"
|
||||
},
|
||||
"copyEmail": {
|
||||
"message": "Copy email"
|
||||
@ -3991,13 +4012,13 @@
|
||||
"message": "Admin Console"
|
||||
},
|
||||
"accountSecurity": {
|
||||
"message": "Account security"
|
||||
"message": "帳戶安全性"
|
||||
},
|
||||
"notifications": {
|
||||
"message": "Notifications"
|
||||
"message": "通知"
|
||||
},
|
||||
"appearance": {
|
||||
"message": "Appearance"
|
||||
"message": "外觀"
|
||||
},
|
||||
"errorAssigningTargetCollection": {
|
||||
"message": "Error assigning target collection."
|
||||
@ -4029,7 +4050,7 @@
|
||||
"message": "New"
|
||||
},
|
||||
"removeItem": {
|
||||
"message": "Remove $NAME$",
|
||||
"message": "移除 $NAME$",
|
||||
"description": "Remove a selected option, such as a folder or collection",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
@ -4155,7 +4176,7 @@
|
||||
"message": "Authenticator key"
|
||||
},
|
||||
"autofillOptions": {
|
||||
"message": "Autofill options"
|
||||
"message": "自動填入選項"
|
||||
},
|
||||
"websiteUri": {
|
||||
"message": "Website (URI)"
|
||||
@ -4229,13 +4250,13 @@
|
||||
}
|
||||
},
|
||||
"enableAnimations": {
|
||||
"message": "Enable animations"
|
||||
"message": "啟用動畫"
|
||||
},
|
||||
"showAnimations": {
|
||||
"message": "Show animations"
|
||||
"message": "顯示動畫"
|
||||
},
|
||||
"addAccount": {
|
||||
"message": "Add account"
|
||||
"message": "新增帳戶"
|
||||
},
|
||||
"loading": {
|
||||
"message": "Loading"
|
||||
@ -4360,7 +4381,7 @@
|
||||
}
|
||||
},
|
||||
"selectCollectionsToAssign": {
|
||||
"message": "Select collections to assign"
|
||||
"message": "選擇要指派的集合"
|
||||
},
|
||||
"personalItemTransferWarningSingular": {
|
||||
"message": "1 item will be permanently transferred to the selected organization. You will no longer own this item."
|
||||
@ -4397,7 +4418,7 @@
|
||||
}
|
||||
},
|
||||
"successfullyAssignedCollections": {
|
||||
"message": "Successfully assigned collections"
|
||||
"message": "指派集合成功"
|
||||
},
|
||||
"nothingSelected": {
|
||||
"message": "You have not selected anything."
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
AnonLayoutWrapperDataService,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Icon, IconModule } from "@bitwarden/components";
|
||||
import { Icon, IconModule, Translation } from "@bitwarden/components";
|
||||
|
||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||
@ -90,11 +90,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (firstChildRouteData["pageTitle"] !== undefined) {
|
||||
this.pageTitle = this.i18nService.t(firstChildRouteData["pageTitle"]);
|
||||
this.pageTitle = this.handleStringOrTranslation(firstChildRouteData["pageTitle"]);
|
||||
}
|
||||
|
||||
if (firstChildRouteData["pageSubtitle"] !== undefined) {
|
||||
this.pageSubtitle = this.i18nService.t(firstChildRouteData["pageSubtitle"]);
|
||||
this.pageSubtitle = this.handleStringOrTranslation(firstChildRouteData["pageSubtitle"]);
|
||||
}
|
||||
|
||||
if (firstChildRouteData["pageIcon"] !== undefined) {
|
||||
@ -132,19 +132,11 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (data.pageTitle) {
|
||||
this.pageTitle = this.i18nService.t(data.pageTitle);
|
||||
this.pageTitle = this.handleStringOrTranslation(data.pageTitle);
|
||||
}
|
||||
|
||||
if (data.pageSubtitle) {
|
||||
// If you pass just a string, we translate it by default
|
||||
if (typeof data.pageSubtitle === "string") {
|
||||
this.pageSubtitle = this.i18nService.t(data.pageSubtitle);
|
||||
} else {
|
||||
// if you pass an object, you can specify if you want to translate it or not
|
||||
this.pageSubtitle = data.pageSubtitle.translate
|
||||
? this.i18nService.t(data.pageSubtitle.subtitle)
|
||||
: data.pageSubtitle.subtitle;
|
||||
}
|
||||
this.pageSubtitle = this.handleStringOrTranslation(data.pageSubtitle);
|
||||
}
|
||||
|
||||
if (data.pageIcon) {
|
||||
@ -168,6 +160,16 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private handleStringOrTranslation(value: string | Translation): string {
|
||||
if (typeof value === "string") {
|
||||
// If it's a string, return it as is
|
||||
return value;
|
||||
}
|
||||
|
||||
// If it's a Translation object, translate it
|
||||
return this.i18nService.t(value.key, ...(value.placeholders ?? []));
|
||||
}
|
||||
|
||||
private resetPageData() {
|
||||
this.pageTitle = null;
|
||||
this.pageSubtitle = null;
|
||||
|
@ -221,8 +221,12 @@ export const DefaultContentExample: Story = {
|
||||
|
||||
// Dynamic Content Example
|
||||
const initialData: ExtensionAnonLayoutWrapperData = {
|
||||
pageTitle: "setAStrongPassword",
|
||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
||||
pageTitle: {
|
||||
key: "setAStrongPassword",
|
||||
},
|
||||
pageSubtitle: {
|
||||
key: "finishCreatingYourAccountBySettingAPassword",
|
||||
},
|
||||
pageIcon: LockIcon,
|
||||
showAcctSwitcher: true,
|
||||
showBackButton: true,
|
||||
@ -230,8 +234,12 @@ const initialData: ExtensionAnonLayoutWrapperData = {
|
||||
};
|
||||
|
||||
const changedData: ExtensionAnonLayoutWrapperData = {
|
||||
pageTitle: "enterpriseSingleSignOn",
|
||||
pageSubtitle: "checkYourEmail",
|
||||
pageTitle: {
|
||||
key: "enterpriseSingleSignOn",
|
||||
},
|
||||
pageSubtitle: {
|
||||
key: "checkYourEmail",
|
||||
},
|
||||
pageIcon: RegistrationCheckEmailIcon,
|
||||
showAcctSwitcher: false,
|
||||
showBackButton: false,
|
||||
|
@ -188,6 +188,8 @@ export type OverlayBackgroundExtensionMessageHandlers = {
|
||||
updateIsFieldCurrentlyFilling: ({ message }: BackgroundMessageParam) => void;
|
||||
checkIsFieldCurrentlyFilling: () => boolean;
|
||||
getAutofillInlineMenuVisibility: () => void;
|
||||
getInlineMenuCardsVisibility: () => void;
|
||||
getInlineMenuIdentitiesVisibility: () => void;
|
||||
openAutofillInlineMenu: () => void;
|
||||
closeAutofillInlineMenu: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
||||
checkAutofillInlineMenuFocused: ({ sender }: BackgroundSenderParam) => void;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { CLEAR_NOTIFICATION_LOGIN_DATA_DURATION } from "@bitwarden/common/autofill/constants";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@ -24,6 +25,7 @@ import { OverlayNotificationsBackground } from "./overlay-notifications.backgrou
|
||||
|
||||
describe("OverlayNotificationsBackground", () => {
|
||||
let logService: MockProxy<LogService>;
|
||||
let getFeatureFlagMock$: BehaviorSubject<boolean>;
|
||||
let configService: MockProxy<ConfigService>;
|
||||
let notificationBackground: NotificationBackground;
|
||||
let getEnableChangedPasswordPromptSpy: jest.SpyInstance;
|
||||
@ -33,7 +35,10 @@ describe("OverlayNotificationsBackground", () => {
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers();
|
||||
logService = mock<LogService>();
|
||||
configService = mock<ConfigService>();
|
||||
getFeatureFlagMock$ = new BehaviorSubject(true);
|
||||
configService = mock<ConfigService>({
|
||||
getFeatureFlag$: jest.fn().mockReturnValue(getFeatureFlagMock$),
|
||||
});
|
||||
notificationBackground = mock<NotificationBackground>();
|
||||
getEnableChangedPasswordPromptSpy = jest
|
||||
.spyOn(notificationBackground, "getEnableChangedPasswordPrompt")
|
||||
@ -164,8 +169,17 @@ describe("OverlayNotificationsBackground", () => {
|
||||
});
|
||||
|
||||
describe("storing the modified login form data", () => {
|
||||
const pageDetails = mock<AutofillPageDetails>({ fields: [mock<AutofillField>()] });
|
||||
const sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
|
||||
|
||||
beforeEach(async () => {
|
||||
sendMockExtensionMessage(
|
||||
{ command: "collectPageDetailsResponse", details: pageDetails },
|
||||
sender,
|
||||
);
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
it("stores the modified login cipher form data", async () => {
|
||||
sendMockExtensionMessage(
|
||||
{
|
||||
@ -349,8 +363,14 @@ describe("OverlayNotificationsBackground", () => {
|
||||
|
||||
describe("web requests that trigger notifications", () => {
|
||||
const requestId = "123345";
|
||||
const pageDetails = mock<AutofillPageDetails>({ fields: [mock<AutofillField>()] });
|
||||
|
||||
beforeEach(async () => {
|
||||
sendMockExtensionMessage(
|
||||
{ command: "collectPageDetailsResponse", details: pageDetails },
|
||||
sender,
|
||||
);
|
||||
await flushPromises();
|
||||
sendMockExtensionMessage(
|
||||
{
|
||||
command: "formFieldSubmitted",
|
||||
@ -446,6 +466,11 @@ describe("OverlayNotificationsBackground", () => {
|
||||
|
||||
it("triggers the notification on the beforeRequest listener when a post-submission redirection is encountered", async () => {
|
||||
sender.tab = mock<chrome.tabs.Tab>({ id: 4 });
|
||||
sendMockExtensionMessage(
|
||||
{ command: "collectPageDetailsResponse", details: pageDetails },
|
||||
sender,
|
||||
);
|
||||
await flushPromises();
|
||||
sendMockExtensionMessage(
|
||||
{
|
||||
command: "formFieldSubmitted",
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Subject, switchMap, timer } from "rxjs";
|
||||
import { startWith, Subject, Subscription, switchMap, timer } from "rxjs";
|
||||
import { pairwise } from "rxjs/operators";
|
||||
|
||||
import { CLEAR_NOTIFICATION_LOGIN_DATA_DURATION } from "@bitwarden/common/autofill/constants";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@ -23,7 +24,9 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
private websiteOriginsWithFields: WebsiteOriginsWithFields = new Map();
|
||||
private activeFormSubmissionRequests: ActiveFormSubmissionRequests = new Set();
|
||||
private modifyLoginCipherFormData: ModifyLoginCipherFormDataForTab = new Map();
|
||||
private featureFlagState$: Subscription;
|
||||
private clearLoginCipherFormDataSubject: Subject<void> = new Subject();
|
||||
private notificationFallbackTimeout: number | NodeJS.Timeout | null;
|
||||
private readonly formSubmissionRequestMethods: Set<string> = new Set(["POST", "PUT", "PATCH"]);
|
||||
private readonly extensionMessageHandlers: OverlayNotificationsExtensionMessageHandlers = {
|
||||
formFieldSubmitted: ({ message, sender }) => this.storeModifiedLoginFormData(message, sender),
|
||||
@ -41,19 +44,35 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
* Initialize the overlay notifications background service.
|
||||
*/
|
||||
async init() {
|
||||
const featureFlagActive = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.NotificationBarAddLoginImprovements,
|
||||
);
|
||||
if (!featureFlagActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupExtensionListeners();
|
||||
this.featureFlagState$ = this.configService
|
||||
.getFeatureFlag$(FeatureFlag.NotificationBarAddLoginImprovements)
|
||||
.pipe(startWith(undefined), pairwise())
|
||||
.subscribe(([prev, current]) => this.handleInitFeatureFlagChange(prev, current));
|
||||
this.clearLoginCipherFormDataSubject
|
||||
.pipe(switchMap(() => timer(CLEAR_NOTIFICATION_LOGIN_DATA_DURATION)))
|
||||
.subscribe(() => this.modifyLoginCipherFormData.clear());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles enabling/disabling the extension listeners that trigger the
|
||||
* overlay notifications based on the feature flag state.
|
||||
*
|
||||
* @param previousValue - The previous value of the feature flag
|
||||
* @param currentValue - The current value of the feature flag
|
||||
*/
|
||||
private handleInitFeatureFlagChange = (previousValue: boolean, currentValue: boolean) => {
|
||||
if (previousValue === currentValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentValue) {
|
||||
this.setupExtensionListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
this.removeExtensionListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the response from the content script with the page details. Triggers an initialization
|
||||
* of the add login or change password notification if the conditions are met.
|
||||
@ -126,6 +145,10 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
message: OverlayNotificationsExtensionMessage,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
) => {
|
||||
if (!this.websiteOriginsWithFields.has(sender.tab.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { uri, username, password, newPassword } = message;
|
||||
if (!username && !password && !newPassword) {
|
||||
return;
|
||||
@ -142,8 +165,29 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
}
|
||||
|
||||
this.modifyLoginCipherFormData.set(sender.tab.id, formData);
|
||||
|
||||
this.clearNotificationFallbackTimeout();
|
||||
this.notificationFallbackTimeout = setTimeout(
|
||||
() =>
|
||||
this.setupNotificationInitTrigger(
|
||||
sender.tab.id,
|
||||
"",
|
||||
this.modifyLoginCipherFormData.get(sender.tab.id),
|
||||
).catch((error) => this.logService.error(error)),
|
||||
1500,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the timeout used when triggering a notification on click of the submit button.
|
||||
*/
|
||||
private clearNotificationFallbackTimeout() {
|
||||
if (this.notificationFallbackTimeout) {
|
||||
clearTimeout(this.notificationFallbackTimeout);
|
||||
this.notificationFallbackTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the sender of the message is from an excluded domain. This is used to prevent the
|
||||
* add login or change password notification from being triggered on the user's vault domain or
|
||||
@ -306,12 +350,16 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
private handleOnCompletedRequestEvent = async (details: chrome.webRequest.WebResponseDetails) => {
|
||||
if (
|
||||
this.requestHostIsInvalid(details) ||
|
||||
isInvalidResponseStatusCode(details.statusCode) ||
|
||||
!this.activeFormSubmissionRequests.has(details.requestId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInvalidResponseStatusCode(details.statusCode)) {
|
||||
this.clearNotificationFallbackTimeout();
|
||||
return;
|
||||
}
|
||||
|
||||
const modifyLoginData = this.modifyLoginCipherFormData.get(details.tabId);
|
||||
if (!modifyLoginData) {
|
||||
return;
|
||||
@ -335,6 +383,8 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
requestId: string,
|
||||
modifyLoginData: ModifyLoginCipherFormData,
|
||||
) => {
|
||||
this.clearNotificationFallbackTimeout();
|
||||
|
||||
const tab = await BrowserApi.getTab(tabId);
|
||||
if (tab.status !== "complete") {
|
||||
await this.delayNotificationInitUntilTabIsComplete(tabId, requestId, modifyLoginData);
|
||||
@ -463,11 +513,20 @@ export class OverlayNotificationsBackground implements OverlayNotificationsBackg
|
||||
* Sets up the listeners for the extension messages and the tab events.
|
||||
*/
|
||||
private setupExtensionListeners() {
|
||||
BrowserApi.messageListener("overlay-notifications", this.handleExtensionMessage);
|
||||
BrowserApi.addListener(chrome.runtime.onMessage, this.handleExtensionMessage);
|
||||
chrome.tabs.onRemoved.addListener(this.handleTabRemoved);
|
||||
chrome.tabs.onUpdated.addListener(this.handleTabUpdated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listeners for the extension messages and the tab events.
|
||||
*/
|
||||
private removeExtensionListeners() {
|
||||
BrowserApi.removeListener(chrome.runtime.onMessage, this.handleExtensionMessage);
|
||||
chrome.tabs.onRemoved.removeListener(this.handleTabRemoved);
|
||||
chrome.tabs.onUpdated.removeListener(this.handleTabUpdated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles messages that are sent to the extension background.
|
||||
*
|
||||
|
@ -132,6 +132,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
updateIsFieldCurrentlyFilling: ({ message }) => this.updateIsFieldCurrentlyFilling(message),
|
||||
checkIsFieldCurrentlyFilling: () => this.checkIsFieldCurrentlyFilling(),
|
||||
getAutofillInlineMenuVisibility: () => this.getInlineMenuVisibility(),
|
||||
getInlineMenuCardsVisibility: () => this.getInlineMenuCardsVisibility(),
|
||||
getInlineMenuIdentitiesVisibility: () => this.getInlineMenuIdentitiesVisibility(),
|
||||
openAutofillInlineMenu: () => this.openInlineMenu(false),
|
||||
closeAutofillInlineMenu: ({ message, sender }) => this.closeInlineMenu(sender, message),
|
||||
checkAutofillInlineMenuFocused: ({ sender }) => this.checkInlineMenuFocused(sender),
|
||||
@ -365,7 +367,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.cardAndIdentityCiphers.size) {
|
||||
if (!this.cardAndIdentityCiphers?.size) {
|
||||
this.cardAndIdentityCiphers = null;
|
||||
}
|
||||
|
||||
@ -1484,9 +1486,21 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's authentication status from the auth service. If the user's authentication
|
||||
* status has changed, the inline menu button's authentication status will be updated
|
||||
* and the inline menu list's ciphers will be updated.
|
||||
* Gets the inline menu's visibility setting for Cards from the settings service.
|
||||
*/
|
||||
private async getInlineMenuCardsVisibility(): Promise<boolean> {
|
||||
return await firstValueFrom(this.autofillSettingsService.showInlineMenuCards$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inline menu's visibility setting for Identities from the settings service.
|
||||
*/
|
||||
private async getInlineMenuIdentitiesVisibility(): Promise<boolean> {
|
||||
return await firstValueFrom(this.autofillSettingsService.showInlineMenuIdentities$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's authentication status from the auth service.
|
||||
*/
|
||||
private async getAuthStatus() {
|
||||
return await firstValueFrom(this.authService.activeAccountStatus$);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
import { AutofillOverlayElementType } from "../../enums/autofill-overlay.enum";
|
||||
@ -23,7 +24,7 @@ export type AutofillExtensionMessage = {
|
||||
data?: {
|
||||
direction?: "previous" | "next" | "current";
|
||||
forceCloseInlineMenu?: boolean;
|
||||
inlineMenuVisibility?: number;
|
||||
newSettingValue?: InlineMenuVisibilitySetting;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,6 @@ type Fido2BackgroundExtensionMessageHandlers = {
|
||||
|
||||
interface Fido2Background {
|
||||
init(): void;
|
||||
injectFido2ContentScriptsInAllTabs(): Promise<void>;
|
||||
}
|
||||
|
||||
export {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { Fido2ActiveRequestManager } from "@bitwarden/common/platform/abstractions/fido2/fido2-active-request-manager.abstraction";
|
||||
import {
|
||||
@ -59,6 +61,8 @@ describe("Fido2Background", () => {
|
||||
let scriptInjectorServiceMock!: MockProxy<BrowserScriptInjectorService>;
|
||||
let configServiceMock!: MockProxy<ConfigService>;
|
||||
let enablePasskeysMock$!: BehaviorSubject<boolean>;
|
||||
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
|
||||
let authServiceMock!: MockProxy<AuthService>;
|
||||
let fido2Background!: Fido2Background;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -81,6 +85,9 @@ describe("Fido2Background", () => {
|
||||
vaultSettingsService.enablePasskeys$ = enablePasskeysMock$;
|
||||
fido2ActiveRequestManager = mock<Fido2ActiveRequestManager>();
|
||||
fido2ClientService.isFido2FeatureEnabled.mockResolvedValue(true);
|
||||
activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Unlocked);
|
||||
authServiceMock = mock<AuthService>();
|
||||
authServiceMock.activeAccountStatus$ = activeAccountStatusMock$;
|
||||
fido2Background = new Fido2Background(
|
||||
logService,
|
||||
fido2ActiveRequestManager,
|
||||
@ -88,6 +95,7 @@ describe("Fido2Background", () => {
|
||||
vaultSettingsService,
|
||||
scriptInjectorServiceMock,
|
||||
configServiceMock,
|
||||
authServiceMock,
|
||||
);
|
||||
fido2Background["abortManager"] = abortManagerMock;
|
||||
abortManagerMock.runWithAbortController.mockImplementation((_requestId, runner) =>
|
||||
@ -101,55 +109,31 @@ describe("Fido2Background", () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("injectFido2ContentScriptsInAllTabs", () => {
|
||||
it("does not inject any FIDO2 content scripts when no tabs have a secure url protocol", async () => {
|
||||
const insecureTab = mock<chrome.tabs.Tab>({ id: 789, url: "http://example.com" });
|
||||
tabsQuerySpy.mockResolvedValueOnce([insecureTab]);
|
||||
describe("handleAuthStatusUpdate", () => {
|
||||
let updateContentScriptRegistrationSpy: jest.SpyInstance;
|
||||
|
||||
await fido2Background.injectFido2ContentScriptsInAllTabs();
|
||||
|
||||
expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalled();
|
||||
beforeEach(() => {
|
||||
updateContentScriptRegistrationSpy = jest
|
||||
.spyOn(fido2Background as any, "updateContentScriptRegistration")
|
||||
.mockImplementation();
|
||||
});
|
||||
|
||||
it("only injects the FIDO2 content script into tabs that contain a secure url protocol", async () => {
|
||||
const secondTabMock = mock<chrome.tabs.Tab>({ id: 456, url: "https://example.com" });
|
||||
const insecureTab = mock<chrome.tabs.Tab>({ id: 789, url: "http://example.com" });
|
||||
const noUrlTab = mock<chrome.tabs.Tab>({ id: 101, url: undefined });
|
||||
tabsQuerySpy.mockResolvedValueOnce([tabMock, secondTabMock, insecureTab, noUrlTab]);
|
||||
it("skips triggering the passkeys settings update if the user is logged out", async () => {
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.LoggedOut);
|
||||
|
||||
await fido2Background.injectFido2ContentScriptsInAllTabs();
|
||||
fido2Background.init();
|
||||
await flushPromises();
|
||||
|
||||
expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({
|
||||
tabId: tabMock.id,
|
||||
injectDetails: contentScriptDetails,
|
||||
});
|
||||
expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({
|
||||
tabId: secondTabMock.id,
|
||||
injectDetails: contentScriptDetails,
|
||||
});
|
||||
expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalledWith({
|
||||
tabId: insecureTab.id,
|
||||
injectDetails: contentScriptDetails,
|
||||
});
|
||||
expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalledWith({
|
||||
tabId: noUrlTab.id,
|
||||
injectDetails: contentScriptDetails,
|
||||
});
|
||||
expect(updateContentScriptRegistrationSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("injects the `page-script.js` content script into the provided tab", async () => {
|
||||
tabsQuerySpy.mockResolvedValueOnce([tabMock]);
|
||||
it("triggers the passkeys setting update if the user is logged in", async () => {
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
|
||||
|
||||
await fido2Background.injectFido2ContentScriptsInAllTabs();
|
||||
fido2Background.init();
|
||||
await flushPromises();
|
||||
|
||||
expect(scriptInjectorServiceMock.inject).toHaveBeenCalledWith({
|
||||
tabId: tabMock.id,
|
||||
injectDetails: sharedScriptInjectionDetails,
|
||||
mv2Details: { file: Fido2ContentScript.PageScriptAppend },
|
||||
mv3Details: { file: Fido2ContentScript.PageScript, world: "MAIN" },
|
||||
});
|
||||
expect(updateContentScriptRegistrationSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -157,6 +141,7 @@ describe("Fido2Background", () => {
|
||||
let portMock!: MockProxy<chrome.runtime.Port>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(fido2Background as any, "handleAuthStatusUpdate").mockImplementation();
|
||||
fido2Background.init();
|
||||
jest.spyOn(BrowserApi, "registerContentScriptsMv2");
|
||||
jest.spyOn(BrowserApi, "registerContentScriptsMv3");
|
||||
@ -168,6 +153,15 @@ describe("Fido2Background", () => {
|
||||
tabsQuerySpy.mockResolvedValue([tabMock]);
|
||||
});
|
||||
|
||||
it("skips handling the passkey update if the user is logged out", async () => {
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.LoggedOut);
|
||||
|
||||
enablePasskeysMock$.next(true);
|
||||
|
||||
expect(portMock.disconnect).not.toHaveBeenCalled();
|
||||
expect(scriptInjectorServiceMock.inject).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not destroy and re-inject the content scripts when triggering `handleEnablePasskeysUpdate` with an undefined currentEnablePasskeysSetting property", async () => {
|
||||
await flushPromises();
|
||||
|
||||
@ -421,6 +415,7 @@ describe("Fido2Background", () => {
|
||||
let portMock!: MockProxy<chrome.runtime.Port>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(fido2Background as any, "handleAuthStatusUpdate").mockImplementation();
|
||||
fido2Background.init();
|
||||
portMock = createPortSpyMock(Fido2PortName.InjectedScript);
|
||||
triggerRuntimeOnConnectEvent(portMock);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { firstValueFrom, startWith } from "rxjs";
|
||||
import { firstValueFrom, startWith, Subscription } from "rxjs";
|
||||
import { pairwise } from "rxjs/operators";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { Fido2ActiveRequestManager } from "@bitwarden/common/platform/abstractions/fido2/fido2-active-request-manager.abstraction";
|
||||
@ -29,6 +31,7 @@ import {
|
||||
} from "./abstractions/fido2.background";
|
||||
|
||||
export class Fido2Background implements Fido2BackgroundInterface {
|
||||
private currentAuthStatus$: Subscription;
|
||||
private abortManager = new AbortManager();
|
||||
private fido2ContentScriptPortsSet = new Set<chrome.runtime.Port>();
|
||||
private registeredContentScripts: browser.contentScripts.RegisteredContentScript;
|
||||
@ -55,6 +58,7 @@ export class Fido2Background implements Fido2BackgroundInterface {
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
private scriptInjectorService: ScriptInjectorService,
|
||||
private configService: ConfigService,
|
||||
private authService: AuthService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -68,12 +72,32 @@ export class Fido2Background implements Fido2BackgroundInterface {
|
||||
this.vaultSettingsService.enablePasskeys$
|
||||
.pipe(startWith(undefined), pairwise())
|
||||
.subscribe(([previous, current]) => this.handleEnablePasskeysUpdate(previous, current));
|
||||
this.currentAuthStatus$ = this.authService.activeAccountStatus$
|
||||
.pipe(startWith(undefined), pairwise())
|
||||
.subscribe(([_previous, current]) => this.handleAuthStatusUpdate(current));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles initializing the FIDO2 content scripts based on the current
|
||||
* authentication status. We only want to inject the FIDO2 content scripts
|
||||
* if the user is logged in.
|
||||
*
|
||||
* @param authStatus - The current authentication status.
|
||||
*/
|
||||
private async handleAuthStatusUpdate(authStatus: AuthenticationStatus) {
|
||||
if (authStatus === AuthenticationStatus.LoggedOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enablePasskeys = await this.isPasskeySettingEnabled();
|
||||
await this.handleEnablePasskeysUpdate(enablePasskeys, enablePasskeys);
|
||||
this.currentAuthStatus$.unsubscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the FIDO2 content and page script into all existing browser tabs.
|
||||
*/
|
||||
async injectFido2ContentScriptsInAllTabs() {
|
||||
private async injectFido2ContentScriptsInAllTabs() {
|
||||
const tabs = await BrowserApi.tabsQuery({});
|
||||
|
||||
for (let index = 0; index < tabs.length; index++) {
|
||||
@ -85,6 +109,13 @@ export class Fido2Background implements Fido2BackgroundInterface {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's authentication status from the auth service.
|
||||
*/
|
||||
private async getAuthStatus() {
|
||||
return await firstValueFrom(this.authService.activeAccountStatus$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles reacting to the enablePasskeys setting being updated. If the setting
|
||||
* is enabled, the FIDO2 content scripts are injected into all tabs. If the setting
|
||||
@ -98,13 +129,17 @@ export class Fido2Background implements Fido2BackgroundInterface {
|
||||
previousEnablePasskeysSetting: boolean,
|
||||
enablePasskeys: boolean,
|
||||
) {
|
||||
this.fido2ActiveRequestManager.removeAllActiveRequests();
|
||||
await this.updateContentScriptRegistration();
|
||||
if ((await this.getAuthStatus()) === AuthenticationStatus.LoggedOut) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousEnablePasskeysSetting === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fido2ActiveRequestManager.removeAllActiveRequests();
|
||||
await this.updateContentScriptRegistration();
|
||||
|
||||
this.destroyLoadedFido2ContentScripts();
|
||||
if (enablePasskeys) {
|
||||
void this.injectFido2ContentScriptsInAllTabs();
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
const script = globalContext.document.createElement("script");
|
||||
script.src = chrome.runtime.getURL("content/fido2-page-script.js");
|
||||
script.async = false;
|
||||
|
||||
const scriptInsertionPoint =
|
||||
globalContext.document.head || globalContext.document.documentElement;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
const script = globalContext.document.createElement("script");
|
||||
script.src = chrome.runtime.getURL("content/fido2-page-script.js");
|
||||
script.async = false;
|
||||
|
||||
// We are ensuring that the script injection is delayed in the event that we are loading
|
||||
// within an iframe element. This prevents an issue with web mail clients that load content
|
||||
|
@ -4,6 +4,12 @@ import { MessageType } from "./messaging/message";
|
||||
import { Messenger } from "./messaging/messenger";
|
||||
|
||||
(function (globalContext) {
|
||||
if (globalContext.document.currentScript) {
|
||||
globalContext.document.currentScript.parentNode.removeChild(
|
||||
globalContext.document.currentScript,
|
||||
);
|
||||
}
|
||||
|
||||
const shouldExecuteContentScript =
|
||||
globalContext.document.contentType === "text/html" &&
|
||||
(globalContext.document.location.protocol === "https:" ||
|
||||
|
@ -14,7 +14,7 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
let autofillInlineMenuContentService: AutofillInlineMenuContentService;
|
||||
let autofillInit: AutofillInit;
|
||||
let sendExtensionMessageSpy: jest.SpyInstance;
|
||||
let observeBodyMutationsSpy: jest.SpyInstance;
|
||||
let observeContainerMutationsSpy: jest.SpyInstance;
|
||||
const waitForIdleCallback = () =>
|
||||
new Promise((resolve) => globalThis.requestIdleCallback(resolve));
|
||||
|
||||
@ -25,8 +25,8 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
autofillInlineMenuContentService = new AutofillInlineMenuContentService();
|
||||
autofillInit = new AutofillInit(domQueryService, null, autofillInlineMenuContentService);
|
||||
autofillInit.init();
|
||||
observeBodyMutationsSpy = jest.spyOn(
|
||||
autofillInlineMenuContentService["bodyElementMutationObserver"] as any,
|
||||
observeContainerMutationsSpy = jest.spyOn(
|
||||
autofillInlineMenuContentService["containerElementMutationObserver"] as any,
|
||||
"observe",
|
||||
);
|
||||
sendExtensionMessageSpy = jest.spyOn(
|
||||
@ -51,7 +51,7 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
describe("extension message handlers", () => {
|
||||
describe("closeAutofillInlineMenu message handler", () => {
|
||||
beforeEach(() => {
|
||||
observeBodyMutationsSpy.mockImplementation();
|
||||
observeContainerMutationsSpy.mockImplementation();
|
||||
});
|
||||
|
||||
it("closes the inline menu button", async () => {
|
||||
@ -87,9 +87,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
});
|
||||
|
||||
it("closes both inline menu elements and removes the body element mutation observer", async () => {
|
||||
const unobserveBodyElementSpy = jest.spyOn(
|
||||
const unobserveContainerElementSpy = jest.spyOn(
|
||||
autofillInlineMenuContentService as any,
|
||||
"unobserveBodyElement",
|
||||
"unobserveContainerElement",
|
||||
);
|
||||
sendMockExtensionMessage({
|
||||
command: "appendAutofillInlineMenuToDom",
|
||||
@ -104,7 +104,7 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
command: "closeAutofillInlineMenu",
|
||||
});
|
||||
|
||||
expect(unobserveBodyElementSpy).toHaveBeenCalled();
|
||||
expect(unobserveContainerElementSpy).toHaveBeenCalled();
|
||||
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("autofillOverlayElementClosed", {
|
||||
overlayElement: AutofillOverlayElement.Button,
|
||||
});
|
||||
@ -127,7 +127,7 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
.spyOn(autofillInlineMenuContentService as any, "isInlineMenuListVisible")
|
||||
.mockResolvedValue(true);
|
||||
jest.spyOn(globalThis.document.body, "appendChild");
|
||||
observeBodyMutationsSpy.mockImplementation();
|
||||
observeContainerMutationsSpy.mockImplementation();
|
||||
});
|
||||
|
||||
describe("creating the inline menu button", () => {
|
||||
@ -279,7 +279,8 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleBodyElementMutationObserverUpdate", () => {
|
||||
describe("handleContainerElementMutationObserverUpdate", () => {
|
||||
let mockMutationRecord: MockProxy<MutationRecord>;
|
||||
let buttonElement: HTMLElement;
|
||||
let listElement: HTMLElement;
|
||||
let isInlineMenuListVisibleSpy: jest.SpyInstance;
|
||||
@ -289,6 +290,7 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
<div class="overlay-button"></div>
|
||||
<div class="overlay-list"></div>
|
||||
`;
|
||||
mockMutationRecord = mock<MutationRecord>({ target: globalThis.document.body } as any);
|
||||
buttonElement = document.querySelector(".overlay-button") as HTMLElement;
|
||||
listElement = document.querySelector(".overlay-list") as HTMLElement;
|
||||
autofillInlineMenuContentService["buttonElement"] = buttonElement;
|
||||
@ -309,7 +311,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
autofillInlineMenuContentService["buttonElement"] = undefined;
|
||||
autofillInlineMenuContentService["listElement"] = undefined;
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||
@ -323,7 +327,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
)
|
||||
.mockReturnValue(true);
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||
@ -332,14 +338,18 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
it("skips re-arranging the DOM elements if the last child of the body is non-existent", async () => {
|
||||
document.body.innerHTML = "";
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips re-arranging the DOM elements if the last child of the body is the overlay list and the second to last child of the body is the overlay button", async () => {
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||
@ -349,7 +359,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
listElement.remove();
|
||||
isInlineMenuListVisibleSpy.mockResolvedValue(false);
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||
@ -359,7 +371,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
const injectedElement = document.createElement("div");
|
||||
document.body.insertBefore(injectedElement, listElement);
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
||||
@ -371,7 +385,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
it("positions the overlay button before the overlay list if the elements have inserted in incorrect order", async () => {
|
||||
document.body.appendChild(buttonElement);
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
||||
@ -384,7 +400,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
const injectedElement = document.createElement("div");
|
||||
document.body.appendChild(injectedElement);
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
||||
@ -409,7 +427,9 @@ describe("AutofillInlineMenuContentService", () => {
|
||||
1000,
|
||||
);
|
||||
|
||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
||||
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||
mockMutationRecord,
|
||||
]);
|
||||
await waitForIdleCallback();
|
||||
|
||||
expect(persistentLastChild.style.getPropertyValue("z-index")).toBe("2147483646");
|
||||
|
@ -30,7 +30,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
private buttonElement: HTMLElement;
|
||||
private listElement: HTMLElement;
|
||||
private inlineMenuElementsMutationObserver: MutationObserver;
|
||||
private bodyElementMutationObserver: MutationObserver;
|
||||
private containerElementMutationObserver: MutationObserver;
|
||||
private mutationObserverIterations = 0;
|
||||
private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout;
|
||||
private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout;
|
||||
@ -102,7 +102,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
return;
|
||||
}
|
||||
|
||||
this.unobserveBodyElement();
|
||||
this.unobserveContainerElement();
|
||||
this.closeInlineMenuButton();
|
||||
this.closeInlineMenuList();
|
||||
};
|
||||
@ -153,7 +153,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
}
|
||||
|
||||
if (!(await this.isInlineMenuButtonVisible())) {
|
||||
this.appendInlineMenuElementToBody(this.buttonElement);
|
||||
this.appendInlineMenuElementToDom(this.buttonElement);
|
||||
this.updateInlineMenuElementIsVisibleStatus(AutofillOverlayElement.Button, true);
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
}
|
||||
|
||||
if (!(await this.isInlineMenuListVisible())) {
|
||||
this.appendInlineMenuElementToBody(this.listElement);
|
||||
this.appendInlineMenuElementToDom(this.listElement);
|
||||
this.updateInlineMenuElementIsVisibleStatus(AutofillOverlayElement.List, true);
|
||||
}
|
||||
}
|
||||
@ -196,8 +196,15 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
*
|
||||
* @param element - The inline menu element to append to the body element.
|
||||
*/
|
||||
private appendInlineMenuElementToBody(element: HTMLElement) {
|
||||
this.observeBodyElement();
|
||||
private appendInlineMenuElementToDom(element: HTMLElement) {
|
||||
const parentDialogElement = globalThis.document.activeElement?.closest("dialog");
|
||||
if (parentDialogElement && parentDialogElement.open && parentDialogElement.matches(":modal")) {
|
||||
this.observeContainerElement(parentDialogElement);
|
||||
parentDialogElement.appendChild(element);
|
||||
return;
|
||||
}
|
||||
|
||||
this.observeContainerElement(globalThis.document.body);
|
||||
globalThis.document.body.appendChild(element);
|
||||
}
|
||||
|
||||
@ -276,8 +283,8 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
this.handleInlineMenuElementMutationObserverUpdate,
|
||||
);
|
||||
|
||||
this.bodyElementMutationObserver = new MutationObserver(
|
||||
this.handleBodyElementMutationObserverUpdate,
|
||||
this.containerElementMutationObserver = new MutationObserver(
|
||||
this.handleContainerElementMutationObserverUpdate,
|
||||
);
|
||||
};
|
||||
|
||||
@ -306,19 +313,17 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a mutation observer for the body element. The mutation observer is used
|
||||
* to ensure that the inline menu elements are always present at the bottom of the
|
||||
* body element.
|
||||
* Sets up a mutation observer for the element which contains the inline menu.
|
||||
*/
|
||||
private observeBodyElement() {
|
||||
this.bodyElementMutationObserver?.observe(globalThis.document.body, { childList: true });
|
||||
private observeContainerElement(element: HTMLElement) {
|
||||
this.containerElementMutationObserver?.observe(element, { childList: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the mutation observer for the body element.
|
||||
* Disconnects the mutation observer for the element which contains the inline menu.
|
||||
*/
|
||||
private unobserveBodyElement() {
|
||||
this.bodyElementMutationObserver?.disconnect();
|
||||
private unobserveContainerElement() {
|
||||
this.containerElementMutationObserver?.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,11 +375,11 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the mutation observer update for the body element. This method will
|
||||
* ensure that the inline menu elements are always present at the bottom of the
|
||||
* body element.
|
||||
* Handles the mutation observer update for the element that contains the inline menu.
|
||||
* This method will ensure that the inline menu elements are always present at the
|
||||
* bottom of the container.
|
||||
*/
|
||||
private handleBodyElementMutationObserverUpdate = () => {
|
||||
private handleContainerElementMutationObserverUpdate = (mutations: MutationRecord[]) => {
|
||||
if (
|
||||
(!this.buttonElement && !this.listElement) ||
|
||||
this.isTriggeringExcessiveMutationObserverIterations()
|
||||
@ -382,15 +387,18 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
return;
|
||||
}
|
||||
|
||||
requestIdleCallbackPolyfill(this.processBodyElementMutation, { timeout: 500 });
|
||||
const containerElement = mutations[0].target as HTMLElement;
|
||||
requestIdleCallbackPolyfill(() => this.processContainerElementMutation(containerElement), {
|
||||
timeout: 500,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes the mutation of the body element. Will trigger when an
|
||||
* Processes the mutation of the element that contains the inline menu. Will trigger when an
|
||||
* idle moment in the execution of the main thread is detected.
|
||||
*/
|
||||
private processBodyElementMutation = async () => {
|
||||
const lastChild = globalThis.document.body.lastElementChild;
|
||||
private processContainerElementMutation = async (containerElement: HTMLElement) => {
|
||||
const lastChild = containerElement.lastElementChild;
|
||||
const secondToLastChild = lastChild?.previousElementSibling;
|
||||
const lastChildIsInlineMenuList = lastChild === this.listElement;
|
||||
const lastChildIsInlineMenuButton = lastChild === this.buttonElement;
|
||||
@ -424,11 +432,11 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
||||
(lastChildIsInlineMenuList && !secondToLastChildIsInlineMenuButton) ||
|
||||
(lastChildIsInlineMenuButton && isInlineMenuListVisible)
|
||||
) {
|
||||
globalThis.document.body.insertBefore(this.buttonElement, this.listElement);
|
||||
containerElement.insertBefore(this.buttonElement, this.listElement);
|
||||
return;
|
||||
}
|
||||
|
||||
globalThis.document.body.insertBefore(lastChild, this.buttonElement);
|
||||
containerElement.insertBefore(lastChild, this.buttonElement);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -50,7 +50,7 @@
|
||||
<ng-container *ngIf="!displayedCiphers.length">
|
||||
<bit-no-items class="tw-text-main" [icon]="noResultsIcon">
|
||||
<ng-container slot="title">{{ "noMatchingLoginsForSite" | i18n }}</ng-container>
|
||||
<ng-container slot="description">Search or save passkey as new login</ng-container>
|
||||
<ng-container slot="description">{{ "searchSavePasskeyNewLogin" | i18n }}</ng-container>
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
@ -100,8 +100,8 @@
|
||||
<!-- Display when no matching ciphers exist -->
|
||||
<ng-container *ngIf="!displayedCiphers.length">
|
||||
<bit-no-items class="tw-text-main" [icon]="noResultsIcon">
|
||||
<ng-container slot="title">No matching logins for this site</ng-container>
|
||||
<ng-container slot="description">Search or save passkey as new login</ng-container>
|
||||
<ng-container slot="title">{{ "noItemsMatchSearch" | i18n }}</ng-container>
|
||||
<ng-container slot="description">{{ "clearFiltersOrTryAnother" | i18n }}</ng-container>
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
|
@ -46,6 +46,32 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box tw-mb-5" *ngIf="inlineMenuPositioningImprovementsEnabled && inlineMenuIsEnabled">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="show-inline-menu-identities" class="!tw-mr-0">{{
|
||||
"showInlineMenuIdentitiesLabel" | i18n
|
||||
}}</label>
|
||||
<input
|
||||
id="show-inline-menu-identities"
|
||||
type="checkbox"
|
||||
(change)="updateShowInlineMenuIdentities()"
|
||||
[(ngModel)]="showInlineMenuIdentities"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="show-inline-menu-cards" class="!tw-mr-0">{{
|
||||
"showInlineMenuCardsLabel" | i18n
|
||||
}}</label>
|
||||
<input
|
||||
id="show-inline-menu-cards"
|
||||
type="checkbox"
|
||||
(change)="updateShowInlineMenuCards()"
|
||||
[(ngModel)]="showInlineMenuCards"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content" *ngIf="canOverrideBrowserAutofillSetting">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
|
@ -8,10 +8,12 @@ import {
|
||||
InlineMenuVisibilitySetting,
|
||||
ClearClipboardDelaySetting,
|
||||
} from "@bitwarden/common/autofill/types";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import {
|
||||
UriMatchStrategy,
|
||||
UriMatchStrategySetting,
|
||||
} from "@bitwarden/common/models/domain/domain-service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -20,7 +22,6 @@ import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||
import { enableAccountSwitching } from "../../../platform/flags";
|
||||
import { AutofillService } from "../../services/abstractions/autofill.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-autofill-v1",
|
||||
@ -32,6 +33,10 @@ export class AutofillV1Component implements OnInit {
|
||||
protected autoFillOverlayVisibility: InlineMenuVisibilitySetting;
|
||||
protected autoFillOverlayVisibilityOptions: any[];
|
||||
protected disablePasswordManagerLink: string;
|
||||
protected inlineMenuPositioningImprovementsEnabled: boolean = false;
|
||||
protected showInlineMenuIdentities: boolean = true;
|
||||
protected showInlineMenuCards: boolean = true;
|
||||
inlineMenuIsEnabled: boolean = false;
|
||||
enableAutoFillOnPageLoad = false;
|
||||
autoFillOnPageLoadDefault = false;
|
||||
autoFillOnPageLoadOptions: any[];
|
||||
@ -50,7 +55,7 @@ export class AutofillV1Component implements OnInit {
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private autofillService: AutofillService,
|
||||
private configService: ConfigService,
|
||||
private dialogService: DialogService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private messagingService: MessagingService,
|
||||
@ -109,6 +114,20 @@ export class AutofillV1Component implements OnInit {
|
||||
this.autofillSettingsService.inlineMenuVisibility$,
|
||||
);
|
||||
|
||||
this.inlineMenuPositioningImprovementsEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.InlineMenuPositioningImprovements,
|
||||
);
|
||||
|
||||
this.inlineMenuIsEnabled = this.isInlineMenuEnabled();
|
||||
|
||||
this.showInlineMenuIdentities =
|
||||
this.inlineMenuPositioningImprovementsEnabled &&
|
||||
(await firstValueFrom(this.autofillSettingsService.showInlineMenuIdentities$));
|
||||
|
||||
this.showInlineMenuCards =
|
||||
this.inlineMenuPositioningImprovementsEnabled &&
|
||||
(await firstValueFrom(this.autofillSettingsService.showInlineMenuCards$));
|
||||
|
||||
this.enableAutoFillOnPageLoad = await firstValueFrom(
|
||||
this.autofillSettingsService.autofillOnPageLoad$,
|
||||
);
|
||||
@ -140,9 +159,18 @@ export class AutofillV1Component implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
isInlineMenuEnabled() {
|
||||
return (
|
||||
this.autoFillOverlayVisibility === AutofillOverlayVisibility.OnFieldFocus ||
|
||||
this.autoFillOverlayVisibility === AutofillOverlayVisibility.OnButtonClick
|
||||
);
|
||||
}
|
||||
|
||||
async updateAutoFillOverlayVisibility() {
|
||||
await this.autofillSettingsService.setInlineMenuVisibility(this.autoFillOverlayVisibility);
|
||||
await this.requestPrivacyPermission();
|
||||
|
||||
this.inlineMenuIsEnabled = this.isInlineMenuEnabled();
|
||||
}
|
||||
|
||||
async updateAutoFillOnPageLoad() {
|
||||
@ -298,4 +326,12 @@ export class AutofillV1Component implements OnInit {
|
||||
async updateShowIdentitiesCurrentTab() {
|
||||
await this.vaultSettingsService.setShowIdentitiesCurrentTab(this.showIdentitiesCurrentTab);
|
||||
}
|
||||
|
||||
async updateShowInlineMenuCards() {
|
||||
await this.autofillSettingsService.setShowInlineMenuCards(this.showInlineMenuCards);
|
||||
}
|
||||
|
||||
async updateShowInlineMenuIdentities() {
|
||||
await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,37 @@
|
||||
{{ "showInlineMenuOnFormFieldsDescAlt" | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-control>
|
||||
<bit-form-control *ngIf="enableInlineMenu" class="tw-pl-5">
|
||||
<bit-form-control
|
||||
*ngIf="inlineMenuPositioningImprovementsEnabled && enableInlineMenu"
|
||||
class="tw-ml-5"
|
||||
>
|
||||
<input
|
||||
bitCheckbox
|
||||
id="show-inline-menu-identities"
|
||||
type="checkbox"
|
||||
(change)="updateShowInlineMenuIdentities()"
|
||||
[(ngModel)]="showInlineMenuIdentities"
|
||||
/>
|
||||
<bit-label for="show-inline-menu-identities">
|
||||
{{ "showInlineMenuIdentitiesLabel" | i18n }}
|
||||
</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control
|
||||
*ngIf="inlineMenuPositioningImprovementsEnabled && enableInlineMenu"
|
||||
class="tw-ml-5"
|
||||
>
|
||||
<input
|
||||
bitCheckbox
|
||||
id="show-inline-menu-cards"
|
||||
type="checkbox"
|
||||
(change)="updateShowInlineMenuCards()"
|
||||
[(ngModel)]="showInlineMenuCards"
|
||||
/>
|
||||
<bit-label for="show-inline-menu-cards">
|
||||
{{ "showInlineMenuCardsLabel" | i18n }}
|
||||
</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control *ngIf="enableInlineMenu" class="tw-ml-5">
|
||||
<input
|
||||
bitCheckbox
|
||||
id="show-autofill-suggestions-on-icon"
|
||||
|
@ -21,10 +21,12 @@ import {
|
||||
DisablePasswordManagerUri,
|
||||
InlineMenuVisibilitySetting,
|
||||
} from "@bitwarden/common/autofill/types";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import {
|
||||
UriMatchStrategy,
|
||||
UriMatchStrategySetting,
|
||||
} from "@bitwarden/common/models/domain/domain-service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -82,6 +84,7 @@ export class AutofillComponent implements OnInit {
|
||||
protected defaultBrowserAutofillDisabled: boolean = false;
|
||||
protected inlineMenuVisibility: InlineMenuVisibilitySetting =
|
||||
AutofillOverlayVisibility.OnFieldFocus;
|
||||
protected inlineMenuPositioningImprovementsEnabled: boolean = false;
|
||||
protected browserClientVendor: BrowserClientVendor = BrowserClientVendors.Unknown;
|
||||
protected disablePasswordManagerURI: DisablePasswordManagerUri =
|
||||
DisablePasswordManagerUris.Unknown;
|
||||
@ -93,6 +96,8 @@ export class AutofillComponent implements OnInit {
|
||||
enableAutofillOnPageLoad: boolean = false;
|
||||
enableInlineMenu: boolean = false;
|
||||
enableInlineMenuOnIconSelect: boolean = false;
|
||||
showInlineMenuIdentities: boolean = true;
|
||||
showInlineMenuCards: boolean = true;
|
||||
autofillOnPageLoadDefault: boolean = false;
|
||||
autofillOnPageLoadOptions: { name: string; value: boolean }[];
|
||||
enableContextMenuItem: boolean = false;
|
||||
@ -114,6 +119,7 @@ export class AutofillComponent implements OnInit {
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private messagingService: MessagingService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
this.autofillOnPageLoadOptions = [
|
||||
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true },
|
||||
@ -151,6 +157,18 @@ export class AutofillComponent implements OnInit {
|
||||
this.autofillSettingsService.inlineMenuVisibility$,
|
||||
);
|
||||
|
||||
this.inlineMenuPositioningImprovementsEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.InlineMenuPositioningImprovements,
|
||||
);
|
||||
|
||||
this.showInlineMenuIdentities =
|
||||
this.inlineMenuPositioningImprovementsEnabled &&
|
||||
(await firstValueFrom(this.autofillSettingsService.showInlineMenuIdentities$));
|
||||
|
||||
this.showInlineMenuCards =
|
||||
this.inlineMenuPositioningImprovementsEnabled &&
|
||||
(await firstValueFrom(this.autofillSettingsService.showInlineMenuCards$));
|
||||
|
||||
this.enableInlineMenuOnIconSelect =
|
||||
this.inlineMenuVisibility === AutofillOverlayVisibility.OnButtonClick;
|
||||
|
||||
@ -381,4 +399,12 @@ export class AutofillComponent implements OnInit {
|
||||
async updateShowIdentitiesCurrentTab() {
|
||||
await this.vaultSettingsService.setShowIdentitiesCurrentTab(this.showIdentitiesCurrentTab);
|
||||
}
|
||||
|
||||
async updateShowInlineMenuCards() {
|
||||
await this.autofillSettingsService.setShowInlineMenuCards(this.showInlineMenuCards);
|
||||
}
|
||||
|
||||
async updateShowInlineMenuIdentities() {
|
||||
await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities);
|
||||
}
|
||||
}
|
||||
|
@ -2238,7 +2238,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
it("updates the inlineMenuVisibility property", () => {
|
||||
sendMockExtensionMessage({
|
||||
command: "updateAutofillInlineMenuVisibility",
|
||||
data: { inlineMenuVisibility: AutofillOverlayVisibility.OnButtonClick },
|
||||
data: { newSettingValue: AutofillOverlayVisibility.OnButtonClick },
|
||||
});
|
||||
|
||||
expect(autofillOverlayContentService["inlineMenuVisibility"]).toEqual(
|
||||
|
@ -8,7 +8,9 @@ import {
|
||||
AutofillOverlayVisibility,
|
||||
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
||||
AUTOFILL_TRIGGER_FORM_FIELD_SUBMIT,
|
||||
AUTOFILL_OVERLAY_HANDLE_SCROLL,
|
||||
} from "@bitwarden/common/autofill/constants";
|
||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
import {
|
||||
@ -51,7 +53,9 @@ import { AutoFillConstants } from "./autofill-constants";
|
||||
|
||||
export class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface {
|
||||
pageDetailsUpdateRequired = false;
|
||||
inlineMenuVisibility: number;
|
||||
inlineMenuVisibility: InlineMenuVisibilitySetting;
|
||||
private showInlineMenuIdentities: boolean;
|
||||
private showInlineMenuCards: boolean;
|
||||
private readonly findTabs = tabbable;
|
||||
private readonly sendExtensionMessage = sendExtensionMessage;
|
||||
private formFieldElements: Map<ElementWithOpId<FormFieldElement>, AutofillField> = new Map();
|
||||
@ -183,6 +187,18 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
autofillFieldData: AutofillField,
|
||||
pageDetails: AutofillPageDetails,
|
||||
) {
|
||||
if (!this.inlineMenuVisibility) {
|
||||
await this.getInlineMenuVisibility();
|
||||
}
|
||||
|
||||
if (this.showInlineMenuCards == null) {
|
||||
await this.getInlineMenuCardsVisibility();
|
||||
}
|
||||
|
||||
if (this.showInlineMenuIdentities == null) {
|
||||
await this.getInlineMenuIdentitiesVisibility();
|
||||
}
|
||||
|
||||
if (
|
||||
this.formFieldElements.has(formFieldElement) ||
|
||||
this.isIgnoredField(autofillFieldData, pageDetails)
|
||||
@ -1019,10 +1035,16 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
const { width, height, top, left } =
|
||||
await this.getMostRecentlyFocusedFieldRects(formFieldElement);
|
||||
const autofillFieldData = this.formFieldElements.get(formFieldElement);
|
||||
|
||||
let accountCreationFieldType = null;
|
||||
|
||||
if (
|
||||
// user setting allows display of identities in inline menu
|
||||
this.showInlineMenuIdentities &&
|
||||
// `showInlineMenuAccountCreation` has been set or field is filled by Login cipher
|
||||
(autofillFieldData?.showInlineMenuAccountCreation ||
|
||||
autofillFieldData?.filledByCipherType === CipherType.Login) &&
|
||||
// field is a username field, which is relevant to both Identity and Login ciphers
|
||||
this.inlineMenuFieldQualificationService.isUsernameField(autofillFieldData)
|
||||
) {
|
||||
accountCreationFieldType = this.inlineMenuFieldQualificationService.isEmailField(
|
||||
@ -1125,6 +1147,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
}
|
||||
|
||||
if (
|
||||
this.showInlineMenuCards &&
|
||||
this.inlineMenuFieldQualificationService.isFieldForCreditCardForm(
|
||||
autofillFieldData,
|
||||
pageDetails,
|
||||
@ -1135,6 +1158,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
}
|
||||
|
||||
if (
|
||||
this.showInlineMenuIdentities &&
|
||||
this.inlineMenuFieldQualificationService.isFieldForAccountCreationForm(
|
||||
autofillFieldData,
|
||||
pageDetails,
|
||||
@ -1146,6 +1170,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
}
|
||||
|
||||
if (
|
||||
this.showInlineMenuIdentities &&
|
||||
this.inlineMenuFieldQualificationService.isFieldForIdentityForm(
|
||||
autofillFieldData,
|
||||
pageDetails,
|
||||
@ -1244,6 +1269,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
autofillFieldData.readonly = getAttributeBoolean(formFieldElement, "disabled");
|
||||
autofillFieldData.disabled = getAttributeBoolean(formFieldElement, "disabled");
|
||||
autofillFieldData.viewable = true;
|
||||
|
||||
void this.setupOverlayListenersOnQualifiedField(formFieldElement, autofillFieldData);
|
||||
}
|
||||
|
||||
@ -1266,10 +1292,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
await this.updateMostRecentlyFocusedField(formFieldElement);
|
||||
}
|
||||
|
||||
if (!this.inlineMenuVisibility) {
|
||||
await this.getInlineMenuVisibility();
|
||||
}
|
||||
|
||||
this.setupFormFieldElementEventListeners(formFieldElement);
|
||||
this.setupFormSubmissionEventListeners(formFieldElement, autofillFieldData);
|
||||
|
||||
@ -1291,6 +1313,30 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
this.inlineMenuVisibility = inlineMenuVisibility || AutofillOverlayVisibility.OnFieldFocus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the background script for the autofill inline menu's Cards visibility setting.
|
||||
* If the setting is not found, a default value of true will be used
|
||||
* @private
|
||||
*/
|
||||
private async getInlineMenuCardsVisibility() {
|
||||
const inlineMenuCardsVisibility = await this.sendExtensionMessage(
|
||||
"getInlineMenuCardsVisibility",
|
||||
);
|
||||
this.showInlineMenuCards = inlineMenuCardsVisibility ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the background script for the autofill inline menu's Identities visibility setting.
|
||||
* If the setting is not found, a default value of true will be used
|
||||
* @private
|
||||
*/
|
||||
private async getInlineMenuIdentitiesVisibility() {
|
||||
const inlineMenuIdentitiesVisibility = await this.sendExtensionMessage(
|
||||
"getInlineMenuIdentitiesVisibility",
|
||||
);
|
||||
this.showInlineMenuIdentities = inlineMenuIdentitiesVisibility ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value that indicates if we should hide the inline menu list due to a filled field.
|
||||
*
|
||||
@ -1318,8 +1364,10 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
* @param data - The data object from the extension message.
|
||||
*/
|
||||
private updateInlineMenuVisibility({ data }: AutofillExtensionMessage) {
|
||||
if (!isNaN(data?.inlineMenuVisibility)) {
|
||||
this.inlineMenuVisibility = data.inlineMenuVisibility;
|
||||
const newSettingValue = data?.newSettingValue;
|
||||
|
||||
if (!isNaN(newSettingValue)) {
|
||||
this.inlineMenuVisibility = newSettingValue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1600,15 +1648,28 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
* the overlay elements on scroll or resize.
|
||||
*/
|
||||
private setOverlayRepositionEventListeners() {
|
||||
const handler = this.useEventHandlersMemo(
|
||||
const repositionHandler = this.useEventHandlersMemo(
|
||||
throttle(this.handleOverlayRepositionEvent, 250),
|
||||
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
||||
);
|
||||
globalThis.addEventListener(EVENTS.SCROLL, handler, {
|
||||
|
||||
const eventTargetDoesNotContainFocusedField = (element: Element) =>
|
||||
typeof element?.contains === "function" && !element.contains(this.mostRecentlyFocusedField);
|
||||
const scrollHandler = this.useEventHandlersMemo(
|
||||
throttle((event) => {
|
||||
if (eventTargetDoesNotContainFocusedField(event.target as Element)) {
|
||||
return;
|
||||
}
|
||||
repositionHandler(event);
|
||||
}, 50),
|
||||
AUTOFILL_OVERLAY_HANDLE_SCROLL,
|
||||
);
|
||||
|
||||
globalThis.addEventListener(EVENTS.SCROLL, scrollHandler, {
|
||||
capture: true,
|
||||
passive: true,
|
||||
});
|
||||
globalThis.addEventListener(EVENTS.RESIZE, handler);
|
||||
globalThis.addEventListener(EVENTS.RESIZE, repositionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1616,12 +1677,19 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
* the overlay elements on scroll or resize.
|
||||
*/
|
||||
private removeOverlayRepositionEventListeners() {
|
||||
const handler = this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION];
|
||||
globalThis.removeEventListener(EVENTS.SCROLL, handler, {
|
||||
capture: true,
|
||||
});
|
||||
globalThis.removeEventListener(EVENTS.RESIZE, handler);
|
||||
globalThis.removeEventListener(
|
||||
EVENTS.SCROLL,
|
||||
this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_SCROLL],
|
||||
{
|
||||
capture: true,
|
||||
},
|
||||
);
|
||||
globalThis.removeEventListener(
|
||||
EVENTS.RESIZE,
|
||||
this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION],
|
||||
);
|
||||
|
||||
delete this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_SCROLL];
|
||||
delete this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION];
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,8 @@ describe("AutofillService", () => {
|
||||
let autofillService: AutofillService;
|
||||
const cipherService = mock<CipherService>();
|
||||
let inlineMenuVisibilityMock$!: BehaviorSubject<InlineMenuVisibilitySetting>;
|
||||
let showInlineMenuCardsMock$!: BehaviorSubject<boolean>;
|
||||
let showInlineMenuIdentitiesMock$!: BehaviorSubject<boolean>;
|
||||
let autofillSettingsService: MockProxy<AutofillSettingsServiceAbstraction>;
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
@ -98,8 +100,12 @@ describe("AutofillService", () => {
|
||||
beforeEach(() => {
|
||||
scriptInjectorService = new BrowserScriptInjectorService(platformUtilsService, logService);
|
||||
inlineMenuVisibilityMock$ = new BehaviorSubject(AutofillOverlayVisibility.OnFieldFocus);
|
||||
showInlineMenuCardsMock$ = new BehaviorSubject(false);
|
||||
showInlineMenuIdentitiesMock$ = new BehaviorSubject(false);
|
||||
autofillSettingsService = mock<AutofillSettingsServiceAbstraction>();
|
||||
autofillSettingsService.inlineMenuVisibility$ = inlineMenuVisibilityMock$;
|
||||
autofillSettingsService.showInlineMenuCards$ = showInlineMenuCardsMock$;
|
||||
autofillSettingsService.showInlineMenuIdentities$ = showInlineMenuIdentitiesMock$;
|
||||
activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Unlocked);
|
||||
authService = mock<AuthService>();
|
||||
authService.activeAccountStatus$ = activeAccountStatusMock$;
|
||||
@ -291,12 +297,12 @@ describe("AutofillService", () => {
|
||||
expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith(
|
||||
tab1,
|
||||
"updateAutofillInlineMenuVisibility",
|
||||
{ inlineMenuVisibility: AutofillOverlayVisibility.OnButtonClick },
|
||||
{ newSettingValue: AutofillOverlayVisibility.OnButtonClick },
|
||||
);
|
||||
expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith(
|
||||
tab2,
|
||||
"updateAutofillInlineMenuVisibility",
|
||||
{ inlineMenuVisibility: AutofillOverlayVisibility.OnButtonClick },
|
||||
{ newSettingValue: AutofillOverlayVisibility.OnButtonClick },
|
||||
);
|
||||
});
|
||||
|
||||
@ -308,12 +314,12 @@ describe("AutofillService", () => {
|
||||
expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith(
|
||||
tab1,
|
||||
"updateAutofillInlineMenuVisibility",
|
||||
{ inlineMenuVisibility: AutofillOverlayVisibility.OnFieldFocus },
|
||||
{ newSettingValue: AutofillOverlayVisibility.OnFieldFocus },
|
||||
);
|
||||
expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith(
|
||||
tab2,
|
||||
"updateAutofillInlineMenuVisibility",
|
||||
{ inlineMenuVisibility: AutofillOverlayVisibility.OnFieldFocus },
|
||||
{ newSettingValue: AutofillOverlayVisibility.OnFieldFocus },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -130,10 +130,23 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
async loadAutofillScriptsOnInstall() {
|
||||
BrowserApi.addListener(chrome.runtime.onConnect, this.handleInjectedScriptPortConnection);
|
||||
void this.injectAutofillScriptsInAllTabs();
|
||||
|
||||
this.autofillSettingsService.inlineMenuVisibility$
|
||||
.pipe(startWith(undefined), pairwise())
|
||||
.subscribe(([previousSetting, currentSetting]) =>
|
||||
this.handleInlineMenuVisibilityChange(previousSetting, currentSetting),
|
||||
this.handleInlineMenuVisibilitySettingsChange(previousSetting, currentSetting),
|
||||
);
|
||||
|
||||
this.autofillSettingsService.showInlineMenuCards$
|
||||
.pipe(startWith(undefined), pairwise())
|
||||
.subscribe(([previousSetting, currentSetting]) =>
|
||||
this.handleInlineMenuVisibilitySettingsChange(previousSetting, currentSetting),
|
||||
);
|
||||
|
||||
this.autofillSettingsService.showInlineMenuIdentities$
|
||||
.pipe(startWith(undefined), pairwise())
|
||||
.subscribe(([previousSetting, currentSetting]) =>
|
||||
this.handleInlineMenuVisibilitySettingsChange(previousSetting, currentSetting),
|
||||
);
|
||||
}
|
||||
|
||||
@ -3043,27 +3056,36 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the autofill inline menu visibility setting in all active tabs
|
||||
* when the InlineMenuVisibilitySetting observable is updated.
|
||||
* Updates the autofill inline menu visibility settings in all active tabs
|
||||
* when the inlineMenuVisibility, showInlineMenuCards, or showInlineMenuIdentities
|
||||
* observables are updated.
|
||||
*
|
||||
* @param previousSetting - The previous setting value
|
||||
* @param currentSetting - The current setting value
|
||||
* @param oldSettingValue - The previous setting value
|
||||
* @param newSettingValue - The current setting value
|
||||
* @param cipherType - The cipher type of the changed inline menu setting
|
||||
*/
|
||||
private async handleInlineMenuVisibilityChange(
|
||||
previousSetting: InlineMenuVisibilitySetting,
|
||||
currentSetting: InlineMenuVisibilitySetting,
|
||||
private async handleInlineMenuVisibilitySettingsChange(
|
||||
oldSettingValue: InlineMenuVisibilitySetting | boolean,
|
||||
newSettingValue: InlineMenuVisibilitySetting | boolean,
|
||||
) {
|
||||
if (previousSetting === undefined || previousSetting === currentSetting) {
|
||||
if (oldSettingValue === undefined || oldSettingValue === newSettingValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inlineMenuPreviouslyDisabled = previousSetting === AutofillOverlayVisibility.Off;
|
||||
const inlineMenuCurrentlyDisabled = currentSetting === AutofillOverlayVisibility.Off;
|
||||
if (!inlineMenuPreviouslyDisabled && !inlineMenuCurrentlyDisabled) {
|
||||
const isInlineMenuVisibilitySubSetting =
|
||||
typeof oldSettingValue === "boolean" || typeof newSettingValue === "boolean";
|
||||
const inlineMenuPreviouslyDisabled = oldSettingValue === AutofillOverlayVisibility.Off;
|
||||
const inlineMenuCurrentlyDisabled = newSettingValue === AutofillOverlayVisibility.Off;
|
||||
|
||||
if (
|
||||
!isInlineMenuVisibilitySubSetting &&
|
||||
!inlineMenuPreviouslyDisabled &&
|
||||
!inlineMenuCurrentlyDisabled
|
||||
) {
|
||||
const tabs = await BrowserApi.tabsQuery({});
|
||||
tabs.forEach((tab) =>
|
||||
BrowserApi.tabSendMessageData(tab, "updateAutofillInlineMenuVisibility", {
|
||||
inlineMenuVisibility: currentSetting,
|
||||
newSettingValue,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
|
@ -7,6 +7,27 @@ import { DomQueryService as DomQueryServiceInterface } from "./abstractions/dom-
|
||||
export class DomQueryService implements DomQueryServiceInterface {
|
||||
private pageContainsShadowDom: boolean;
|
||||
private useTreeWalkerStrategyFlagSet = true;
|
||||
private ignoredTreeWalkerNodes = new Set([
|
||||
"svg",
|
||||
"script",
|
||||
"noscript",
|
||||
"head",
|
||||
"style",
|
||||
"link",
|
||||
"meta",
|
||||
"title",
|
||||
"base",
|
||||
"img",
|
||||
"picture",
|
||||
"video",
|
||||
"audio",
|
||||
"object",
|
||||
"source",
|
||||
"track",
|
||||
"param",
|
||||
"map",
|
||||
"area",
|
||||
]);
|
||||
|
||||
constructor() {
|
||||
void this.init();
|
||||
@ -21,6 +42,7 @@ export class DomQueryService implements DomQueryServiceInterface {
|
||||
* @param treeWalkerFilter - The filter callback to use for the treeWalker query
|
||||
* @param mutationObserver - The MutationObserver to use for observing shadow roots
|
||||
* @param forceDeepQueryAttempt - Whether to force a deep query attempt
|
||||
* @param ignoredTreeWalkerNodesOverride - An optional set of node names to ignore when using the treeWalker strategy
|
||||
*/
|
||||
query<T>(
|
||||
root: Document | ShadowRoot | Element,
|
||||
@ -28,15 +50,28 @@ export class DomQueryService implements DomQueryServiceInterface {
|
||||
treeWalkerFilter: CallableFunction,
|
||||
mutationObserver?: MutationObserver,
|
||||
forceDeepQueryAttempt?: boolean,
|
||||
ignoredTreeWalkerNodesOverride?: Set<string>,
|
||||
): T[] {
|
||||
const ignoredTreeWalkerNodes = ignoredTreeWalkerNodesOverride || this.ignoredTreeWalkerNodes;
|
||||
|
||||
if (!forceDeepQueryAttempt && this.pageContainsShadowDomElements()) {
|
||||
return this.queryAllTreeWalkerNodes<T>(root, treeWalkerFilter, mutationObserver);
|
||||
return this.queryAllTreeWalkerNodes<T>(
|
||||
root,
|
||||
treeWalkerFilter,
|
||||
ignoredTreeWalkerNodes,
|
||||
mutationObserver,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return this.deepQueryElements<T>(root, queryString, mutationObserver);
|
||||
} catch {
|
||||
return this.queryAllTreeWalkerNodes<T>(root, treeWalkerFilter, mutationObserver);
|
||||
return this.queryAllTreeWalkerNodes<T>(
|
||||
root,
|
||||
treeWalkerFilter,
|
||||
ignoredTreeWalkerNodes,
|
||||
mutationObserver,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,11 +242,13 @@ export class DomQueryService implements DomQueryServiceInterface {
|
||||
* and returns a collection of nodes.
|
||||
* @param rootNode
|
||||
* @param filterCallback
|
||||
* @param ignoredTreeWalkerNodes
|
||||
* @param mutationObserver
|
||||
*/
|
||||
private queryAllTreeWalkerNodes<T>(
|
||||
rootNode: Node,
|
||||
filterCallback: CallableFunction,
|
||||
ignoredTreeWalkerNodes: Set<string>,
|
||||
mutationObserver?: MutationObserver,
|
||||
): T[] {
|
||||
const treeWalkerQueryResults: T[] = [];
|
||||
@ -220,6 +257,7 @@ export class DomQueryService implements DomQueryServiceInterface {
|
||||
rootNode,
|
||||
treeWalkerQueryResults,
|
||||
filterCallback,
|
||||
ignoredTreeWalkerNodes,
|
||||
mutationObserver,
|
||||
);
|
||||
|
||||
@ -233,15 +271,21 @@ export class DomQueryService implements DomQueryServiceInterface {
|
||||
* @param rootNode
|
||||
* @param treeWalkerQueryResults
|
||||
* @param filterCallback
|
||||
* @param ignoredTreeWalkerNodes
|
||||
* @param mutationObserver
|
||||
*/
|
||||
private buildTreeWalkerNodesQueryResults<T>(
|
||||
rootNode: Node,
|
||||
treeWalkerQueryResults: T[],
|
||||
filterCallback: CallableFunction,
|
||||
ignoredTreeWalkerNodes: Set<string>,
|
||||
mutationObserver?: MutationObserver,
|
||||
) {
|
||||
const treeWalker = document?.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT);
|
||||
const treeWalker = document?.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT, (node) =>
|
||||
ignoredTreeWalkerNodes.has(node.nodeName?.toLowerCase())
|
||||
? NodeFilter.FILTER_REJECT
|
||||
: NodeFilter.FILTER_ACCEPT,
|
||||
);
|
||||
let currentNode = treeWalker?.currentNode;
|
||||
|
||||
while (currentNode) {
|
||||
@ -263,6 +307,7 @@ export class DomQueryService implements DomQueryServiceInterface {
|
||||
nodeShadowRoot,
|
||||
treeWalkerQueryResults,
|
||||
filterCallback,
|
||||
ignoredTreeWalkerNodes,
|
||||
mutationObserver,
|
||||
);
|
||||
}
|
||||
|
@ -1103,6 +1103,7 @@ export default class MainBackground {
|
||||
this.vaultSettingsService,
|
||||
this.scriptInjectorService,
|
||||
this.configService,
|
||||
this.authService,
|
||||
);
|
||||
|
||||
const lockService = new DefaultLockService(this.accountService, this.vaultTimeoutService);
|
||||
@ -1118,7 +1119,6 @@ export default class MainBackground {
|
||||
this.messagingService,
|
||||
this.logService,
|
||||
this.configService,
|
||||
this.fido2Background,
|
||||
messageListener,
|
||||
this.accountService,
|
||||
lockService,
|
||||
@ -1340,7 +1340,7 @@ export default class MainBackground {
|
||||
}
|
||||
|
||||
if (!supported) {
|
||||
this.sdkService.failedToInitialize().catch(this.logService.error);
|
||||
this.sdkService.failedToInitialize().catch((e) => this.logService.error(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ import {
|
||||
openTwoFactorAuthPopout,
|
||||
} from "../auth/popup/utils/auth-popout-window";
|
||||
import { LockedVaultPendingNotificationsData } from "../autofill/background/abstractions/notification.background";
|
||||
import { Fido2Background } from "../autofill/fido2/background/abstractions/fido2.background";
|
||||
import { AutofillService } from "../autofill/services/abstractions/autofill.service";
|
||||
import { BrowserApi } from "../platform/browser/browser-api";
|
||||
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
|
||||
@ -46,7 +45,6 @@ export default class RuntimeBackground {
|
||||
private messagingService: MessagingService,
|
||||
private logService: LogService,
|
||||
private configService: ConfigService,
|
||||
private fido2Background: Fido2Background,
|
||||
private messageListener: MessageListener,
|
||||
private accountService: AccountService,
|
||||
private readonly lockService: LockService,
|
||||
@ -365,7 +363,6 @@ export default class RuntimeBackground {
|
||||
|
||||
private async checkOnInstalled() {
|
||||
setTimeout(async () => {
|
||||
void this.fido2Background.injectFido2ContentScriptsInAllTabs();
|
||||
void this.autofillService.loadAutofillScriptsOnInstall();
|
||||
|
||||
if (this.onInstalledReason != null) {
|
||||
|
@ -43,23 +43,17 @@ function buildRegisterContentScriptsPolyfill() {
|
||||
function NestedProxy<T extends object>(target: T): T {
|
||||
return new Proxy(target, {
|
||||
get(target, prop) {
|
||||
const propertyValue = target[prop as keyof T];
|
||||
|
||||
if (!propertyValue) {
|
||||
if (!target[prop as keyof T]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof propertyValue === "object") {
|
||||
return NestedProxy<typeof propertyValue>(propertyValue);
|
||||
}
|
||||
|
||||
if (typeof propertyValue !== "function") {
|
||||
return propertyValue;
|
||||
if (typeof target[prop as keyof T] !== "function") {
|
||||
return NestedProxy(target[prop as keyof T] as object);
|
||||
}
|
||||
|
||||
return (...arguments_: any[]) =>
|
||||
new Promise((resolve, reject) => {
|
||||
propertyValue(...arguments_, (result: any) => {
|
||||
(target[prop as keyof T] as CallableFunction)(...arguments_, (result: any) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(new Error(chrome.runtime.lastError.message));
|
||||
} else {
|
||||
|
@ -449,7 +449,9 @@ const routes: Routes = [
|
||||
canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
|
||||
data: {
|
||||
pageIcon: LockIcon,
|
||||
pageTitle: "yourVaultIsLockedV2",
|
||||
pageTitle: {
|
||||
key: "yourVaultIsLockedV2",
|
||||
},
|
||||
showReadonlyHostname: true,
|
||||
showAcctSwitcher: true,
|
||||
} satisfies ExtensionAnonLayoutWrapperData,
|
||||
@ -471,7 +473,9 @@ const routes: Routes = [
|
||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||
data: {
|
||||
state: "signup",
|
||||
pageTitle: "createAccount",
|
||||
pageTitle: {
|
||||
key: "createAccount",
|
||||
},
|
||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||
children: [
|
||||
{
|
||||
@ -492,8 +496,12 @@ const routes: Routes = [
|
||||
path: "finish-signup",
|
||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||
data: {
|
||||
pageTitle: "setAStrongPassword",
|
||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
||||
pageTitle: {
|
||||
key: "setAStrongPassword",
|
||||
},
|
||||
pageSubtitle: {
|
||||
key: "finishCreatingYourAccountBySettingAPassword",
|
||||
},
|
||||
state: "finish-signup",
|
||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||
children: [
|
||||
@ -508,8 +516,12 @@ const routes: Routes = [
|
||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification)],
|
||||
component: SetPasswordJitComponent,
|
||||
data: {
|
||||
pageTitle: "joinOrganization",
|
||||
pageSubtitle: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
||||
pageTitle: {
|
||||
key: "joinOrganization",
|
||||
},
|
||||
pageSubtitle: {
|
||||
key: "finishJoiningThisOrganizationBySettingAMasterPassword",
|
||||
},
|
||||
state: "set-password-jit",
|
||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||
},
|
||||
|
@ -81,7 +81,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
.subscribe((supported) => {
|
||||
if (!supported) {
|
||||
this.logService.debug("SDK is not supported");
|
||||
this.sdkService.failedToInitialize().catch(this.logService.error);
|
||||
this.sdkService.failedToInitialize().catch((e) => this.logService.error(e));
|
||||
} else {
|
||||
this.logService.debug("SDK is supported");
|
||||
}
|
||||
|
@ -563,8 +563,14 @@ const safeProviders: SafeProvider[] = [
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ForegroundTaskSchedulerService,
|
||||
useFactory: getBgService<ForegroundTaskSchedulerService>("taskSchedulerService"),
|
||||
deps: [],
|
||||
useFactory: (logService: LogService, stateProvider: StateProvider) => {
|
||||
if (needsBackgroundInit) {
|
||||
return getBgService<ForegroundTaskSchedulerService>("taskSchedulerService")();
|
||||
}
|
||||
|
||||
return new ForegroundTaskSchedulerService(logService, stateProvider);
|
||||
},
|
||||
deps: [LogService, StateProvider],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AnonLayoutWrapperDataService,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ItemModule } from "@bitwarden/components";
|
||||
@ -22,6 +23,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
|
||||
PopupHeaderComponent,
|
||||
PopupPageComponent,
|
||||
PopupFooterComponent,
|
||||
RouterModule,
|
||||
ItemModule,
|
||||
],
|
||||
})
|
||||
|
@ -16,7 +16,9 @@
|
||||
>
|
||||
<bit-icon [icon]="sendCreatedIcon"></bit-icon>
|
||||
<h3 class="tw-font-semibold">{{ "createdSendSuccessfully" | i18n }}</h3>
|
||||
<p class="tw-text-center">{{ "sendAvailability" | i18n: daysAvailable }}</p>
|
||||
<p class="tw-text-center">
|
||||
{{ formatExpirationDate() }}
|
||||
</p>
|
||||
<button bitButton type="button" buttonType="primary" (click)="copyLink()">
|
||||
<b>{{ "copyLink" | i18n }}</b>
|
||||
</button>
|
||||
|
@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { ActivatedRoute, Router, RouterLink } from "@angular/router";
|
||||
import { RouterTestingModule } from "@angular/router/testing";
|
||||
import { MockProxy, mock } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
import { BehaviorSubject, of } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@ -13,7 +13,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
||||
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
|
||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
import { ButtonModule, IconModule, ToastService } from "@bitwarden/components";
|
||||
import { ButtonModule, I18nMockService, IconModule, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component";
|
||||
import { PopupFooterComponent } from "../../../../platform/popup/layout/popup-footer.component";
|
||||
@ -26,7 +26,6 @@ import { SendCreatedComponent } from "./send-created.component";
|
||||
describe("SendCreatedComponent", () => {
|
||||
let component: SendCreatedComponent;
|
||||
let fixture: ComponentFixture<SendCreatedComponent>;
|
||||
let i18nService: MockProxy<I18nService>;
|
||||
let platformUtilsService: MockProxy<PlatformUtilsService>;
|
||||
let sendService: MockProxy<SendService>;
|
||||
let toastService: MockProxy<ToastService>;
|
||||
@ -36,17 +35,10 @@ describe("SendCreatedComponent", () => {
|
||||
let router: MockProxy<Router>;
|
||||
|
||||
const sendId = "test-send-id";
|
||||
const deletionDate = new Date();
|
||||
deletionDate.setDate(deletionDate.getDate() + 7);
|
||||
const sendView: SendView = {
|
||||
id: sendId,
|
||||
deletionDate,
|
||||
accessId: "abc",
|
||||
urlB64Key: "123",
|
||||
} as SendView;
|
||||
let sendView: SendView;
|
||||
let sendViewsSubject: BehaviorSubject<SendView[]>;
|
||||
|
||||
beforeEach(async () => {
|
||||
i18nService = mock<I18nService>();
|
||||
platformUtilsService = mock<PlatformUtilsService>();
|
||||
sendService = mock<SendService>();
|
||||
toastService = mock<ToastService>();
|
||||
@ -54,6 +46,17 @@ describe("SendCreatedComponent", () => {
|
||||
activatedRoute = mock<ActivatedRoute>();
|
||||
environmentService = mock<EnvironmentService>();
|
||||
router = mock<Router>();
|
||||
|
||||
sendView = {
|
||||
id: sendId,
|
||||
deletionDate: new Date(),
|
||||
accessId: "abc",
|
||||
urlB64Key: "123",
|
||||
} as SendView;
|
||||
|
||||
sendViewsSubject = new BehaviorSubject<SendView[]>([sendView]);
|
||||
sendService.sendViews$ = sendViewsSubject.asObservable();
|
||||
|
||||
Object.defineProperty(environmentService, "environment$", {
|
||||
configurable: true,
|
||||
get: () => of(new SelfHostedEnvironment({ webVault: "https://example.com" })),
|
||||
@ -65,8 +68,6 @@ describe("SendCreatedComponent", () => {
|
||||
},
|
||||
} as any;
|
||||
|
||||
sendService.sendViews$ = of([sendView]);
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -82,7 +83,25 @@ describe("SendCreatedComponent", () => {
|
||||
SendCreatedComponent,
|
||||
],
|
||||
providers: [
|
||||
{ provide: I18nService, useValue: i18nService },
|
||||
{
|
||||
provide: I18nService,
|
||||
useFactory: () => {
|
||||
return new I18nMockService({
|
||||
back: "back",
|
||||
loading: "loading",
|
||||
copyLink: "copyLink",
|
||||
close: "close",
|
||||
createdSend: "createdSend",
|
||||
createdSendSuccessfully: "createdSendSuccessfully",
|
||||
popOutNewWindow: "popOutNewWindow",
|
||||
sendExpiresInHours: (hours) => `sendExpiresInHours ${hours}`,
|
||||
sendExpiresInHoursSingle: "sendExpiresInHoursSingle",
|
||||
sendExpiresInDays: (days) => `sendExpiresInDays ${days}`,
|
||||
sendExpiresInDaysSingle: "sendExpiresInDaysSingle",
|
||||
sendLinkCopied: "sendLinkCopied",
|
||||
});
|
||||
},
|
||||
},
|
||||
{ provide: PlatformUtilsService, useValue: platformUtilsService },
|
||||
{ provide: SendService, useValue: sendService },
|
||||
{ provide: ToastService, useValue: toastService },
|
||||
@ -94,40 +113,73 @@ describe("SendCreatedComponent", () => {
|
||||
{ provide: Router, useValue: router },
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SendCreatedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should create", () => {
|
||||
fixture.detectChanges();
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should initialize send and daysAvailable", () => {
|
||||
fixture.detectChanges();
|
||||
it("should initialize send, daysAvailable, and hoursAvailable", () => {
|
||||
expect(component["send"]).toBe(sendView);
|
||||
expect(component["daysAvailable"]).toBe(7);
|
||||
expect(component["daysAvailable"]).toBe(0);
|
||||
expect(component["hoursAvailable"]).toBe(0);
|
||||
});
|
||||
|
||||
it("should navigate back to send list on close", async () => {
|
||||
fixture.detectChanges();
|
||||
await component.close();
|
||||
expect(router.navigate).toHaveBeenCalledWith(["/tabs/send"]);
|
||||
});
|
||||
|
||||
describe("getDaysAvailable", () => {
|
||||
it("returns the correct number of days", () => {
|
||||
describe("getHoursAvailable", () => {
|
||||
it("returns the correct number of hours", () => {
|
||||
sendView.deletionDate.setDate(sendView.deletionDate.getDate() + 7);
|
||||
sendViewsSubject.next([sendView]);
|
||||
fixture.detectChanges();
|
||||
expect(component.getDaysAvailable(sendView)).toBe(7);
|
||||
|
||||
expect(component.getHoursAvailable(sendView)).toBeCloseTo(168, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatExpirationDate", () => {
|
||||
it("returns days plural if expiry is more than 24 hours", () => {
|
||||
sendView.deletionDate.setDate(sendView.deletionDate.getDate() + 7);
|
||||
sendViewsSubject.next([sendView]);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.formatExpirationDate()).toBe("sendExpiresInDays 7");
|
||||
});
|
||||
|
||||
it("returns days singular if expiry is 24 hours", () => {
|
||||
sendView.deletionDate.setDate(sendView.deletionDate.getDate() + 1);
|
||||
sendViewsSubject.next([sendView]);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.formatExpirationDate()).toBe("sendExpiresInDaysSingle");
|
||||
});
|
||||
|
||||
it("returns hours plural if expiry is more than 1 hour but less than 24", () => {
|
||||
sendView.deletionDate.setHours(sendView.deletionDate.getHours() + 2);
|
||||
sendViewsSubject.next([sendView]);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.formatExpirationDate()).toBe("sendExpiresInHours 2");
|
||||
});
|
||||
|
||||
it("returns hours singular if expiry is in 1 hour", () => {
|
||||
sendView.deletionDate.setHours(sendView.deletionDate.getHours() + 1);
|
||||
sendViewsSubject.next([sendView]);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.formatExpirationDate()).toBe("sendExpiresInHoursSingle");
|
||||
});
|
||||
});
|
||||
|
||||
describe("copyLink", () => {
|
||||
it("should copy link and show toast", async () => {
|
||||
fixture.detectChanges();
|
||||
const link = "https://example.com/#/send/abc/123";
|
||||
|
||||
await component.copyLink();
|
||||
@ -136,7 +188,7 @@ describe("SendCreatedComponent", () => {
|
||||
expect(toastService.showToast).toHaveBeenCalledWith({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: i18nService.t("sendLinkCopied"),
|
||||
message: "sendLinkCopied",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -39,6 +39,7 @@ export class SendCreatedComponent {
|
||||
protected sendCreatedIcon = SendCreatedIcon;
|
||||
protected send: SendView;
|
||||
protected daysAvailable = 0;
|
||||
protected hoursAvailable = 0;
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
@ -54,14 +55,26 @@ export class SendCreatedComponent {
|
||||
this.sendService.sendViews$.pipe(takeUntilDestroyed()).subscribe((sendViews) => {
|
||||
this.send = sendViews.find((s) => s.id === sendId);
|
||||
if (this.send) {
|
||||
this.daysAvailable = this.getDaysAvailable(this.send);
|
||||
this.hoursAvailable = this.getHoursAvailable(this.send);
|
||||
this.daysAvailable = Math.ceil(this.hoursAvailable / 24);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getDaysAvailable(send: SendView): number {
|
||||
formatExpirationDate(): string {
|
||||
if (this.hoursAvailable < 24) {
|
||||
return this.hoursAvailable === 1
|
||||
? this.i18nService.t("sendExpiresInHoursSingle")
|
||||
: this.i18nService.t("sendExpiresInHours", this.hoursAvailable);
|
||||
}
|
||||
return this.daysAvailable === 1
|
||||
? this.i18nService.t("sendExpiresInDaysSingle")
|
||||
: this.i18nService.t("sendExpiresInDays", this.daysAvailable);
|
||||
}
|
||||
|
||||
getHoursAvailable(send: SendView): number {
|
||||
const now = new Date().getTime();
|
||||
return Math.max(0, Math.ceil((send.deletionDate.getTime() - now) / (1000 * 60 * 60 * 24)));
|
||||
return Math.max(0, Math.ceil((send.deletionDate.getTime() - now) / (1000 * 60 * 60)));
|
||||
}
|
||||
|
||||
async close() {
|
||||
|
@ -9,7 +9,6 @@
|
||||
<bit-item>
|
||||
<button type="button" bit-item-content (click)="about()">
|
||||
{{ "aboutBitwarden" | i18n }}
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user