mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-23 21:31:29 +01:00
[PM-5189] Incorporating work done for inline menu field qualification
This commit is contained in:
commit
ede74bc72d
11
.github/workflows/build-browser.yml
vendored
11
.github/workflows/build-browser.yml
vendored
@ -186,17 +186,10 @@ jobs:
|
||||
# path: browser-source/apps/browser/dist/dist-opera-mv3.zip
|
||||
# if-no-files-found: error
|
||||
|
||||
- name: Upload Chrome artifact
|
||||
- name: Upload Chrome MV3 artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: dist-chrome-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-chrome.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Chrome MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
|
||||
if-no-files-found: error
|
||||
|
||||
|
2
.github/workflows/release-browser.yml
vendored
2
.github/workflows/release-browser.yml
vendored
@ -132,7 +132,7 @@ jobs:
|
||||
PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
|
||||
run: |
|
||||
mv browser-source.zip browser-source-$PACKAGE_VERSION.zip
|
||||
mv dist-chrome.zip dist-chrome-$PACKAGE_VERSION.zip
|
||||
mv dist-chrome-mv3.zip dist-chrome-$PACKAGE_VERSION.zip
|
||||
mv dist-opera.zip dist-opera-$PACKAGE_VERSION.zip
|
||||
mv dist-firefox.zip dist-firefox-$PACKAGE_VERSION.zip
|
||||
mv dist-edge.zip dist-edge-$PACKAGE_VERSION.zip
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/browser",
|
||||
"version": "2024.6.0",
|
||||
"version": "2024.6.1",
|
||||
"scripts": {
|
||||
"build": "cross-env MANIFEST_VERSION=3 webpack",
|
||||
"build:mv2": "webpack",
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "الهوية"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "سجل كلمة المرور"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Sevimlilərdən sil"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Element sevimlilərə əlavə edildi"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Element sevimlilərdən çıxarıldı"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Notlar"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Kimlik"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Yeni $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "$TYPE$ - düzəliş et",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Parol tarixçəsi"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Filtrləri təmizləyin və ya başqa bir axtarış terminini sınayın"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "$ITEMNAME$ elementlərini kopyala",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Məlumatları kopyala - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "$ITEMNAME$ notunu kopyala",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Notu kopyala - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Avto-doldur - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Kopyalanacaq dəyər yoxdur"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Kolleksiyaları təyin et"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Пасведчанне"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Гісторыя пароляў"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Изваждане от любими"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Елементът е добавен към любимите"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Елементът е премахнат от любимите"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Бележки"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Самоличност"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Редактиране на $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Хронология на паролата"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Изчистете филтрите или опитайте да търсите нещо друго"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Копиране на информацията, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Копиране на информацията – $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Копиране на бележката, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Копиране на бележката – $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Авт. попълване – $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Няма стойности за копиране"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Свързване на колекции"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "পরিচয়"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "পাসওয়ার্ড ইতিহাস"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitat"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Historial de les contrasenyes"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Unfavorite"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Položka byla přidána do oblíbených"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Položka byla odebrána z oblíbených"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Poznámky"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identita"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Nové $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Upravit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Historie hesel"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Vymažte filtry nebo zkuste jiný hledaný výraz"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Kopírovat informace, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Kopírovat informace - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Kopírovat poznámku, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Kopírovat poznámku - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Automatické vyplnění - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Žádné hodnoty ke zkopírování"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Hunaniaeth"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Hanes cyfrineiriau"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitet"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Ny $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Redigér $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Adgangskodehistorik"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Ryd filtre eller prøv med et andet søgeord"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Kopiér info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Kopiér info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Kopiér notat, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Kopiér notat - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Autoudfyld - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Ingen værdier at kopiere"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Tildel samlinger"
|
||||
},
|
||||
|
@ -390,13 +390,13 @@
|
||||
"message": "Favoriten"
|
||||
},
|
||||
"unfavorite": {
|
||||
"message": "Unfavorite"
|
||||
"message": "Aus Favoriten entfernen"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Eintrag zu Favoriten hinzugefügt"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Eintrag aus Favoriten entfernt"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Notizen"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identität"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Neue $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "$TYPE$ bearbeiten",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Passwortverlauf"
|
||||
},
|
||||
@ -3305,18 +3323,8 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Entferne die Filter oder versuche einen anderen Suchbegriff"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"message": "Information kopieren - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3325,18 +3333,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"message": "Notiz kopieren - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3346,7 +3344,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsLabel": {
|
||||
"message": "More options, $ITEMNAME$",
|
||||
"message": "Weitere Optionen, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3356,7 +3354,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsTitle": {
|
||||
"message": "More options - $ITEMNAME$",
|
||||
"message": "Weitere Optionen - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3366,7 +3364,7 @@
|
||||
}
|
||||
},
|
||||
"viewItemTitle": {
|
||||
"message": "View item - $ITEMNAME$",
|
||||
"message": "Eintrag anzeigen - $ITEMNAME$",
|
||||
"description": "Title for a link that opens a view for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-Ausfüllen - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Keine Werte zum Kopieren"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Sammlungen zuweisen"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Ταυτότητα"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Ιστορικό Κωδικού"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -16,6 +16,12 @@
|
||||
"createAccount": {
|
||||
"message": "Create account"
|
||||
},
|
||||
"setAStrongPassword": {
|
||||
"message": "Set a strong password"
|
||||
},
|
||||
"finishCreatingYourAccountBySettingAPassword": {
|
||||
"message": "Finish creating your account by setting a password"
|
||||
},
|
||||
"login": {
|
||||
"message": "Log in"
|
||||
},
|
||||
@ -1780,6 +1786,21 @@
|
||||
"masterPasswordPolicyRequirementsNotMet": {
|
||||
"message": "Your new master password does not meet the policy requirements."
|
||||
},
|
||||
"receiveMarketingEmails": {
|
||||
"message": "Get emails from Bitwarden for announcements, advice, and research opportunities."
|
||||
},
|
||||
"unsubscribe": {
|
||||
"message": "Unsubscribe"
|
||||
},
|
||||
"atAnyTime": {
|
||||
"message": "at any time."
|
||||
},
|
||||
"byContinuingYouAgreeToThe": {
|
||||
"message": "By continuing, you agree to the"
|
||||
},
|
||||
"and": {
|
||||
"message": "and"
|
||||
},
|
||||
"acceptPolicies": {
|
||||
"message": "By checking this box you agree to the following:"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Unfavourite"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Item added to favourites"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Item removed from favourites"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Notes"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Eliminar favorito"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Elemento añadido a favoritos"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Elemento eliminado de favoritos"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Notas"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identidad"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Nuevo $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Editar $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Historial de contraseñas"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Limpia los filtros o prueba otro término de búsqueda"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copiar información, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copiar información - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copiar nota, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copiar nota - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Autocompletar - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No hay valores para copiar"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Asignar colecciones"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identiteet"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Paroolide ajalugu"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitatea"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Pasahitz historia"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "هویت"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "تاریخچه کلمه عبور"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -390,13 +390,13 @@
|
||||
"message": "Suosikki"
|
||||
},
|
||||
"unfavorite": {
|
||||
"message": "Unfavorite"
|
||||
"message": "Poista suosikeista"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Kohde lisättiin suosikkeihin"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Kohde poistettiin suosikeista"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Merkinnät"
|
||||
@ -420,7 +420,7 @@
|
||||
"message": "Avaa"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "Launch website"
|
||||
"message": "Avaa verkkosivusto"
|
||||
},
|
||||
"website": {
|
||||
"message": "Verkkosivusto"
|
||||
@ -778,7 +778,7 @@
|
||||
"message": "Avaa"
|
||||
},
|
||||
"additionalOptions": {
|
||||
"message": "Lisäasetukset"
|
||||
"message": "Lisävalinnat"
|
||||
},
|
||||
"enableContextMenuItem": {
|
||||
"message": "Näytä sisältövalikon valinnat"
|
||||
@ -845,7 +845,7 @@
|
||||
"message": "Viennin tyyppi"
|
||||
},
|
||||
"accountRestricted": {
|
||||
"message": "Rajoitettu tilille"
|
||||
"message": "Tiliä on rajoitettu"
|
||||
},
|
||||
"filePasswordAndConfirmFilePasswordDoNotMatch": {
|
||||
"message": "\"Tiedoston salasana\" ja \"Vahvista tiedoston salasana\" eivät täsmää."
|
||||
@ -1120,22 +1120,22 @@
|
||||
"message": "Itse ylläpidetty palvelinympäristö"
|
||||
},
|
||||
"selfHostedEnvironmentFooter": {
|
||||
"message": "Määritä omassa palvelinympäristössäsi suoritettavan Bitwarden-asennuksen pääverkkotunnus."
|
||||
"message": "Määritä omassa palvelinympäristössäsi suoritettavan Bitwarden-asennuksen perusosoite."
|
||||
},
|
||||
"selfHostedBaseUrlHint": {
|
||||
"message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com"
|
||||
"message": "Määritä itse ylläpitämäsi Bitwarden-asennuksen perusosoite. Esimerkki: https://bitwarden.yritys.fi."
|
||||
},
|
||||
"selfHostedCustomEnvHeader": {
|
||||
"message": "For advanced configuration, you can specify the base URL of each service independently."
|
||||
"message": "Edistynyttä määritystä varten voit syöttää jokaisen palvelun perusosoitteen erikseen."
|
||||
},
|
||||
"selfHostedEnvFormInvalid": {
|
||||
"message": "You must add either the base Server URL or at least one custom environment."
|
||||
"message": "Sinun on lisättävä joko palvelimen perusosoite tai ainakin yksi mukautettu palvelinympäristö."
|
||||
},
|
||||
"customEnvironment": {
|
||||
"message": "Mukautettu palvelinympäristö"
|
||||
},
|
||||
"customEnvironmentFooter": {
|
||||
"message": "Edistyneille käyttäjille. Voit määrittää jokaiselle palvelulle oman pääverkkotunnuksen."
|
||||
"message": "Edistyneille käyttäjille. Voit määrittää jokaiselle palvelulle oman perusosoitteen."
|
||||
},
|
||||
"baseUrl": {
|
||||
"message": "Palvelimen URL"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Henkilöllisyys"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Uusi $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Muokkaa $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Salasanahistoria"
|
||||
},
|
||||
@ -1444,7 +1462,7 @@
|
||||
"message": "Kokoelmat"
|
||||
},
|
||||
"nCollections": {
|
||||
"message": "$COUNT$ collections",
|
||||
"message": "$COUNT$ kokoelmaa",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
@ -1682,7 +1700,7 @@
|
||||
"message": "Automaattitäytä ja tallenna"
|
||||
},
|
||||
"fillAndSave": {
|
||||
"message": "Fill and save"
|
||||
"message": "Täytä ja tallenna"
|
||||
},
|
||||
"autoFillSuccessAndSavedUri": {
|
||||
"message": "Kohde täytettiin automaattisesti ja URI tallennettiin"
|
||||
@ -1781,10 +1799,10 @@
|
||||
"message": "Ok"
|
||||
},
|
||||
"errorRefreshingAccessToken": {
|
||||
"message": "Access Token Refresh Error"
|
||||
"message": "Käyttötunnisteen päivitysvirhe"
|
||||
},
|
||||
"errorRefreshingAccessTokenDesc": {
|
||||
"message": "No refresh token or API keys found. Please try logging out and logging back in."
|
||||
"message": "Päivitystunnistetta tai API-avaimia ei löytynyt. Kokeile kirjautua ulos ja takaisin sisään."
|
||||
},
|
||||
"desktopSyncVerificationTitle": {
|
||||
"message": "Työpöytäsynkronoinnin vahvistus"
|
||||
@ -2344,7 +2362,7 @@
|
||||
}
|
||||
},
|
||||
"forwaderInvalidToken": {
|
||||
"message": "Virheellinen $SERVICENAME$ -rajapinnan tunniste",
|
||||
"message": "Virheellinen $SERVICENAME$ API -tunniste",
|
||||
"description": "Displayed when the user's API token is empty or rejected by the forwarding service.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@ -2354,7 +2372,7 @@
|
||||
}
|
||||
},
|
||||
"forwaderInvalidTokenWithMessage": {
|
||||
"message": "Virheellinen $SERVICENAME$ -rajapinnan tunniste: $ERRORMESSAGE$",
|
||||
"message": "Virheellinen $SERVICENAME$ API -tunniste: $ERRORMESSAGE$",
|
||||
"description": "Displayed when the user's API token is rejected by the forwarding service with an error message.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@ -2368,7 +2386,7 @@
|
||||
}
|
||||
},
|
||||
"forwarderNoAccountId": {
|
||||
"message": "$SERVICENAME$ -palvelun peittämän sähköpostitilin tunnistetta ei saatu.",
|
||||
"message": "$SERVICENAME$ -palvelun peittämän sähköpostitilin tunnusta ei saatu.",
|
||||
"description": "Displayed when the forwarding service fails to return an account ID.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@ -3305,18 +3323,8 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Tyhjennä suodattimet tai kokeile toista hakutermiä"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"message": "Kopioi tietoja - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3325,18 +3333,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"message": "Kopioi muistio - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3346,7 +3344,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsLabel": {
|
||||
"message": "More options, $ITEMNAME$",
|
||||
"message": "Lisää valintoja, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3356,7 +3354,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsTitle": {
|
||||
"message": "More options - $ITEMNAME$",
|
||||
"message": "Lisää valintoja - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3366,7 +3364,7 @@
|
||||
}
|
||||
},
|
||||
"viewItemTitle": {
|
||||
"message": "View item - $ITEMNAME$",
|
||||
"message": "Tarkastele kohdetta - $ITEMNAME$",
|
||||
"description": "Title for a link that opens a view for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3375,17 +3373,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
"message": "Määritä kokoelmat"
|
||||
},
|
||||
"copyEmail": {
|
||||
"message": "Copy email"
|
||||
"message": "Kopioi sähköpostiosoite"
|
||||
},
|
||||
"copyPhone": {
|
||||
"message": "Copy phone"
|
||||
"message": "Kopioi puhelinnumero"
|
||||
},
|
||||
"copyAddress": {
|
||||
"message": "Copy address"
|
||||
"message": "Kopioi osoite"
|
||||
},
|
||||
"adminConsole": {
|
||||
"message": "Hallintapaneelista"
|
||||
@ -3439,7 +3450,7 @@
|
||||
}
|
||||
},
|
||||
"itemsWithNoFolder": {
|
||||
"message": "Kohteet, joilla ei ole kansioita"
|
||||
"message": "Kansiottomat kohteet"
|
||||
},
|
||||
"organizationIsDeactivated": {
|
||||
"message": "Organisaatio on poistettu käytöstä"
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Pagkakakilanlan"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Kasaysayan ng Password"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identité"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Historique des mots de passe"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Effacer les filtres ou essayer un autre terme de recherche"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copier les informations, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copier les informations - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copier la note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copier la note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assigner une collection"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identidade"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Historial de contrasinais"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Unfavorite"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "פריט נוסף למועדפים"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "פריט הוסר מהמועדפים"
|
||||
},
|
||||
"notes": {
|
||||
"message": "הערות"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "זהות"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "היסטוריית סיסמאות"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "पहचान"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "नया $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "$TYPE$ संपादन",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "पासवर्ड इतिहास"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitet"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Povijest"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Nem kedvenc"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Az elem bekerült a kedvencekhez."
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Az elem kikerült a kedvencekből."
|
||||
},
|
||||
"notes": {
|
||||
"message": "Jegyzetek"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Személyazonosság"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Új $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "$TYPE$ szerkesztése",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Jelszó előzmények"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Töröljük a szűrőket vagy próbálkozzunk másik keresési kifejezéssel."
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Infó másolása, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Infó másolása - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Jegyzet másolása, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Jegyzet másolása - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Automatikus kitöltés - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Nincsenek másolandó értékek."
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Gyűjtemények hozzárendelése"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitas"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Riwayat Kata Sandi"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identità"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Cronologia delle password"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Cancella i filtri o prova un altro termine di ricerca"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copia informazioni, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copia informazioni - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copia nota, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copia nota - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assegna raccolte"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "ID"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "$TYPE$ を新規作成",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "$TYPE$ を編集",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "パスワードの履歴"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "フィルタをクリアするか、別の検索ワードをお試しください"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "$ITEMNAME$ の情報をコピー",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "情報をコピー - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "$ITEMNAME$ のメモをコピー",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "メモをコピー - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "自動入力 - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "コピーする値がありません"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "コレクションを割り当て"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "ಗುರುತಿಸುವಿಕೆ"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "ಪಾಸ್ವರ್ಡ್ ಇತಿಹಾಸ"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -173,16 +173,16 @@
|
||||
"message": "웹 앱에서 계속하시겠용?"
|
||||
},
|
||||
"continueToWebAppDesc": {
|
||||
"message": "Explore more features of your Bitwarden account on the web app."
|
||||
"message": "웹 앱에서 Bitwarden 계정의 더 많은 기능을 탐색해보세요."
|
||||
},
|
||||
"continueToHelpCenter": {
|
||||
"message": "Continue to Help Center?"
|
||||
"message": "도움말 센터로 이동"
|
||||
},
|
||||
"continueToHelpCenterDesc": {
|
||||
"message": "Learn more about how to use Bitwarden on the Help Center."
|
||||
"message": "Bitwarden의 자세한 사용법은 도움말 센터에서 확인하세요."
|
||||
},
|
||||
"continueToBrowserExtensionStore": {
|
||||
"message": "Continue to browser extension store?"
|
||||
"message": "브라우저 확장 스토어로 이동하시겠습니까?"
|
||||
},
|
||||
"continueToBrowserExtensionStoreDesc": {
|
||||
"message": "Help others find out if Bitwarden is right for them. Visit your browser's extension store and leave a rating now."
|
||||
@ -205,7 +205,7 @@
|
||||
"message": "로그아웃"
|
||||
},
|
||||
"aboutBitwarden": {
|
||||
"message": "About Bitwarden"
|
||||
"message": "Bitwarden 에 대하여"
|
||||
},
|
||||
"about": {
|
||||
"message": "정보"
|
||||
@ -390,7 +390,7 @@
|
||||
"message": "즐겨찾기"
|
||||
},
|
||||
"unfavorite": {
|
||||
"message": "Unfavorite"
|
||||
"message": "즐겨찾기 해제"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
@ -420,7 +420,7 @@
|
||||
"message": "열기"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "Launch website"
|
||||
"message": "웹사이트 열기"
|
||||
},
|
||||
"website": {
|
||||
"message": "웹 사이트"
|
||||
@ -435,7 +435,7 @@
|
||||
"message": "기타"
|
||||
},
|
||||
"unlockMethods": {
|
||||
"message": "Unlock options"
|
||||
"message": "잠금 해제 옵션"
|
||||
},
|
||||
"unlockMethodNeededToChangeTimeoutActionDesc": {
|
||||
"message": "잠금 해제 방법을 설정하여 보관함의 시간 초과 동작을 변경하세요."
|
||||
@ -444,10 +444,10 @@
|
||||
"message": "설정에서 잠금 해제 수단 설정하기"
|
||||
},
|
||||
"sessionTimeoutHeader": {
|
||||
"message": "Session timeout"
|
||||
"message": "세션 만료"
|
||||
},
|
||||
"otherOptions": {
|
||||
"message": "Other options"
|
||||
"message": "기타 옵션"
|
||||
},
|
||||
"rateExtension": {
|
||||
"message": "확장 프로그램 평가"
|
||||
@ -722,7 +722,7 @@
|
||||
"message": "\"로그인 추가 알림\"을 사용하면 새 로그인을 사용할 때마다 보관함에 그 로그인을 추가할 것인지 물어봅니다."
|
||||
},
|
||||
"addLoginNotificationDescAlt": {
|
||||
"message": "Ask to add an item if one isn't found in your vault. Applies to all logged in accounts."
|
||||
"message": "보관함에 항목이 없을 경우 추가하라는 메시지를 표시합니다. 모든 로그인된 계정에 적용됩니다."
|
||||
},
|
||||
"showCardsCurrentTab": {
|
||||
"message": "탭 페이지에 카드 표시"
|
||||
@ -754,10 +754,10 @@
|
||||
"message": "현재 로그인으로 업데이트할 건지 묻기"
|
||||
},
|
||||
"changedPasswordNotificationDesc": {
|
||||
"message": "Ask to update a login's password when a change is detected on a website."
|
||||
"message": "웹사이트에서 변경 사항이 감지되면 로그인 비밀번호를 업데이트하라는 메시지를 표시합니다."
|
||||
},
|
||||
"changedPasswordNotificationDescAlt": {
|
||||
"message": "Ask to update a login's password when a change is detected on a website. Applies to all logged in accounts."
|
||||
"message": "웹사이트에서 변경 사항이 감지되면 로그인 비밀번호를 업데이트하라는 메시지를 표시합니다. 모든 로그인된 계정에 적용됩니다."
|
||||
},
|
||||
"enableUsePasskeys": {
|
||||
"message": "패스키를 저장 및 사용할지 묻기"
|
||||
@ -778,7 +778,7 @@
|
||||
"message": "잠금 해제"
|
||||
},
|
||||
"additionalOptions": {
|
||||
"message": "Additional options"
|
||||
"message": "추가 옵션"
|
||||
},
|
||||
"enableContextMenuItem": {
|
||||
"message": "Show context menu options"
|
||||
@ -803,7 +803,7 @@
|
||||
"message": "애플리케이션의 색상 테마를 변경합니다."
|
||||
},
|
||||
"themeDescAlt": {
|
||||
"message": "Change the application's color theme. Applies to all logged in accounts."
|
||||
"message": "애플리케이션 색상 테마를 변경합니다. 모든 로그인된 계정에 적용됩니다."
|
||||
},
|
||||
"dark": {
|
||||
"message": "어두운 테마",
|
||||
@ -830,7 +830,7 @@
|
||||
"message": "This file export will be password protected and require the file password to decrypt."
|
||||
},
|
||||
"filePassword": {
|
||||
"message": "File password"
|
||||
"message": "파일 비밀번호"
|
||||
},
|
||||
"exportPasswordDescription": {
|
||||
"message": "This password will be used to export and import this file"
|
||||
@ -842,10 +842,10 @@
|
||||
"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"
|
||||
"message": "계정 제한됨"
|
||||
},
|
||||
"filePasswordAndConfirmFilePasswordDoNotMatch": {
|
||||
"message": "“File password” and “Confirm file password“ do not match."
|
||||
@ -1159,28 +1159,28 @@
|
||||
"message": "환경 URL 값을 저장했습니다."
|
||||
},
|
||||
"showAutoFillMenuOnFormFields": {
|
||||
"message": "Show auto-fill menu on form fields",
|
||||
"message": "입력 필드에 자동 완성 메뉴 표시",
|
||||
"description": "Represents the message for allowing the user to enable the auto-fill overlay"
|
||||
},
|
||||
"showAutoFillMenuOnFormFieldsDescAlt": {
|
||||
"message": "Applies to all logged in accounts."
|
||||
"message": "모든 로그인된 계정에 적용됩니다."
|
||||
},
|
||||
"turnOffBrowserBuiltInPasswordManagerSettings": {
|
||||
"message": "Turn off your browser’s built in password manager settings to avoid conflicts."
|
||||
"message": "충돌을 방지하기 위해 브라우저의 기본 암호 관리 설정을 해제합니다."
|
||||
},
|
||||
"turnOffBrowserBuiltInPasswordManagerSettingsLink": {
|
||||
"message": "Edit browser settings."
|
||||
},
|
||||
"autofillOverlayVisibilityOff": {
|
||||
"message": "Off",
|
||||
"message": "끄기",
|
||||
"description": "Overlay setting select option for disabling autofill overlay"
|
||||
},
|
||||
"autofillOverlayVisibilityOnFieldFocus": {
|
||||
"message": "When field is selected (on focus)",
|
||||
"message": "필드가 선택되었을 때 (포커스 상태)",
|
||||
"description": "Overlay appearance select option for showing the field on focus of the input element"
|
||||
},
|
||||
"autofillOverlayVisibilityOnButtonClick": {
|
||||
"message": "When auto-fill icon is selected",
|
||||
"message": "자동 완성 아이콘이 선택되었을 때",
|
||||
"description": "Overlay appearance select option for showing the field on click of the overlay icon"
|
||||
},
|
||||
"enableAutoFillOnPageLoad": {
|
||||
@ -1190,7 +1190,7 @@
|
||||
"message": "로그인 양식을 감지하면 웹 페이지 로드 시 자동 완성을 자동으로 수행합니다."
|
||||
},
|
||||
"experimentalFeature": {
|
||||
"message": "Compromised or untrusted websites can exploit auto-fill on page load."
|
||||
"message": "취약하거나 신뢰할 수 없는 웹사이트 페이지 로드 시 자동 완성이 악용될 수 있습니다."
|
||||
},
|
||||
"learnMoreAboutAutofill": {
|
||||
"message": "Learn more about auto-fill"
|
||||
@ -1273,13 +1273,13 @@
|
||||
"message": "Show a recognizable image next to each login."
|
||||
},
|
||||
"faviconDescAlt": {
|
||||
"message": "Show a recognizable image next to each login. Applies to all logged in accounts."
|
||||
"message": "각 로그인 정보 옆에 인식할 수 있는 이미지를 표시합니다. 모든 로그인된 계정에 적용됩니다."
|
||||
},
|
||||
"enableBadgeCounter": {
|
||||
"message": "Show badge counter"
|
||||
"message": "배지 갯수 표시"
|
||||
},
|
||||
"badgeCounterDesc": {
|
||||
"message": "Indicate how many logins you have for the current web page."
|
||||
"message": "현재 웹 페이지에 저장된 로그인 정보의 수를 표시합니다."
|
||||
},
|
||||
"cardholderName": {
|
||||
"message": "카드 소유자 이름"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "신원"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "비밀번호 변경 기록"
|
||||
},
|
||||
@ -1557,7 +1575,7 @@
|
||||
"description": "ex. Date this item was updated"
|
||||
},
|
||||
"dateCreated": {
|
||||
"message": "Created",
|
||||
"message": "생성됨",
|
||||
"description": "ex. Date this item was created"
|
||||
},
|
||||
"datePasswordUpdated": {
|
||||
@ -1614,7 +1632,7 @@
|
||||
"message": "잘못된 PIN 코드입니다."
|
||||
},
|
||||
"tooManyInvalidPinEntryAttemptsLoggingOut": {
|
||||
"message": "Too many invalid PIN entry attempts. Logging out."
|
||||
"message": "잘못된 PIN 입력 시도가 너무 많습니다. 로그아웃 합니다."
|
||||
},
|
||||
"unlockWithBiometrics": {
|
||||
"message": "생체 인식을 사용하여 잠금 해제"
|
||||
@ -1712,13 +1730,13 @@
|
||||
"message": "마스터 비밀번호 설정"
|
||||
},
|
||||
"currentMasterPass": {
|
||||
"message": "Current master password"
|
||||
"message": "현재 마스터 비밀번호"
|
||||
},
|
||||
"newMasterPass": {
|
||||
"message": "New master password"
|
||||
"message": "새 마스터 비밀번호"
|
||||
},
|
||||
"confirmNewMasterPass": {
|
||||
"message": "Confirm new master password"
|
||||
"message": "새 마스터 비밀번호 확인"
|
||||
},
|
||||
"masterPasswordPolicyInEffect": {
|
||||
"message": "하나 이상의 단체 정책이 마스터 비밀번호가 다음 사항을 따르도록 요구합니다:"
|
||||
@ -2137,7 +2155,7 @@
|
||||
"message": "폴더 선택..."
|
||||
},
|
||||
"noFoldersFound": {
|
||||
"message": "No folders found",
|
||||
"message": "폴더를 찾을 수 없습니다.",
|
||||
"description": "Used as a message within the notification bar when no folders are found"
|
||||
},
|
||||
"orgPermissionsUpdatedMustSetPassword": {
|
||||
@ -2149,7 +2167,7 @@
|
||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||
},
|
||||
"verificationRequired": {
|
||||
"message": "Verification required",
|
||||
"message": "인증 필요",
|
||||
"description": "Default title for the user verification dialog."
|
||||
},
|
||||
"hours": {
|
||||
@ -2310,7 +2328,7 @@
|
||||
"message": "서비스"
|
||||
},
|
||||
"forwardedEmail": {
|
||||
"message": "Forwarded email alias"
|
||||
"message": "포워딩된 이메일 별칭"
|
||||
},
|
||||
"forwardedEmailDesc": {
|
||||
"message": "Generate an email alias with an external forwarding service."
|
||||
@ -2418,7 +2436,7 @@
|
||||
}
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Hostname",
|
||||
"message": "호스트 이름",
|
||||
"description": "Part of a URL."
|
||||
},
|
||||
"apiAccessToken": {
|
||||
@ -2431,7 +2449,7 @@
|
||||
"message": "키 커넥터 오류: 키 커넥터가 사용 가능한지 및 정상적으로 작동하고 있는지 확인해주세요."
|
||||
},
|
||||
"premiumSubcriptionRequired": {
|
||||
"message": "Premium subscription required"
|
||||
"message": "프리미엄 구독이 필요합니다"
|
||||
},
|
||||
"organizationIsDisabled": {
|
||||
"message": "Organization suspended."
|
||||
@ -2458,13 +2476,13 @@
|
||||
"message": "to reset to pre-configured settings"
|
||||
},
|
||||
"serverVersion": {
|
||||
"message": "Server version"
|
||||
"message": "서버 버전"
|
||||
},
|
||||
"selfHostedServer": {
|
||||
"message": "self-hosted"
|
||||
"message": "자체 호스팅"
|
||||
},
|
||||
"thirdParty": {
|
||||
"message": "Third-party"
|
||||
"message": "제 3자"
|
||||
},
|
||||
"thirdPartyServerMessage": {
|
||||
"message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.",
|
||||
@ -2491,13 +2509,13 @@
|
||||
"message": "Logging in as"
|
||||
},
|
||||
"notYou": {
|
||||
"message": "Not you?"
|
||||
"message": "본인이 아닌가요?"
|
||||
},
|
||||
"newAroundHere": {
|
||||
"message": "New around here?"
|
||||
"message": "새로 찾아오셨나요?"
|
||||
},
|
||||
"rememberEmail": {
|
||||
"message": "Remember email"
|
||||
"message": "이메일 기억하기"
|
||||
},
|
||||
"loginWithDevice": {
|
||||
"message": "Log in with device"
|
||||
@ -2575,13 +2593,13 @@
|
||||
"message": "Got it"
|
||||
},
|
||||
"autofillSettings": {
|
||||
"message": "Auto-fill settings"
|
||||
"message": "자동 완성 설정"
|
||||
},
|
||||
"autofillShortcut": {
|
||||
"message": "Auto-fill keyboard shortcut"
|
||||
"message": "자동 완성 키보드 단축키"
|
||||
},
|
||||
"autofillShortcutNotSet": {
|
||||
"message": "The auto-fill shortcut is not set. Change this in the browser's settings."
|
||||
"message": "자동 완성 단축키가 설정되지 않았습니다. 브라우저 설정에서 단축키를 변경하세요."
|
||||
},
|
||||
"autofillShortcutText": {
|
||||
"message": "The auto-fill shortcut is: $COMMAND$. Change this in the browser's settings.",
|
||||
@ -2611,7 +2629,7 @@
|
||||
"message": "Device approval required. Select an approval option below:"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Remember this device"
|
||||
"message": "이 기기 기억하기"
|
||||
},
|
||||
"uncheckIfPublicDevice": {
|
||||
"message": "Uncheck if using a public device"
|
||||
@ -2620,7 +2638,7 @@
|
||||
"message": "Approve from your other device"
|
||||
},
|
||||
"requestAdminApproval": {
|
||||
"message": "Request admin approval"
|
||||
"message": "관리자 승인 필요"
|
||||
},
|
||||
"approveWithMasterPassword": {
|
||||
"message": "Approve with master password"
|
||||
@ -2657,16 +2675,16 @@
|
||||
"message": "Access denied. You do not have permission to view this page."
|
||||
},
|
||||
"general": {
|
||||
"message": "General"
|
||||
"message": "일반"
|
||||
},
|
||||
"display": {
|
||||
"message": "Display"
|
||||
"message": "화면"
|
||||
},
|
||||
"accountSuccessfullyCreated": {
|
||||
"message": "Account successfully created!"
|
||||
"message": "계정이 생성되었습니다!"
|
||||
},
|
||||
"adminApprovalRequested": {
|
||||
"message": "Admin approval requested"
|
||||
"message": "관리자 승인 필요"
|
||||
},
|
||||
"adminApprovalRequestSentToAdmins": {
|
||||
"message": "Your request has been sent to your admin."
|
||||
@ -2693,7 +2711,7 @@
|
||||
"message": "required"
|
||||
},
|
||||
"search": {
|
||||
"message": "Search"
|
||||
"message": "검색"
|
||||
},
|
||||
"inputMinLength": {
|
||||
"message": "Input must be at least $COUNT$ characters long.",
|
||||
@ -2741,7 +2759,7 @@
|
||||
}
|
||||
},
|
||||
"multipleInputEmails": {
|
||||
"message": "1 or more emails are invalid"
|
||||
"message": "하나 이상의 이메일이 유효하지 않습니다."
|
||||
},
|
||||
"inputTrimValidator": {
|
||||
"message": "Input must not contain only whitespace.",
|
||||
@ -2811,7 +2829,7 @@
|
||||
"description": "Notification message for when an import is in progress."
|
||||
},
|
||||
"dataSuccessfullyImported": {
|
||||
"message": "Data successfully imported!",
|
||||
"message": "데이터 가져오기 성공!",
|
||||
"description": "Notification message for when an import has completed successfully."
|
||||
},
|
||||
"dataImportFailed": {
|
||||
@ -2857,7 +2875,7 @@
|
||||
"description": "Text to display in overlay when the account is locked."
|
||||
},
|
||||
"unlockAccount": {
|
||||
"message": "Unlock account",
|
||||
"message": "계정 잠금 해제",
|
||||
"description": "Button text to display in overlay when the account is locked."
|
||||
},
|
||||
"fillCredentialsFor": {
|
||||
@ -2873,7 +2891,7 @@
|
||||
"description": "Text to show in overlay if there are no matching items"
|
||||
},
|
||||
"newItem": {
|
||||
"message": "New item",
|
||||
"message": "새 항목",
|
||||
"description": "Button text to display in overlay when there are no matching items"
|
||||
},
|
||||
"addNewVaultItem": {
|
||||
@ -2919,7 +2937,7 @@
|
||||
}
|
||||
},
|
||||
"tryAgain": {
|
||||
"message": "Try again"
|
||||
"message": "다시 시도"
|
||||
},
|
||||
"verificationRequiredForActionSetPinToContinue": {
|
||||
"message": "Verification required for this action. Set a PIN to continue."
|
||||
@ -2979,13 +2997,13 @@
|
||||
"message": "Popout extension"
|
||||
},
|
||||
"launchDuo": {
|
||||
"message": "Launch Duo"
|
||||
"message": "Duo 실행"
|
||||
},
|
||||
"importFormatError": {
|
||||
"message": "Data is not formatted correctly. Please check your import file and try again."
|
||||
"message": "데이터의 포맷이 올바르지 않습니다. 불러올 파일을 확인하고 다시 시도해 주십시오."
|
||||
},
|
||||
"importNothingError": {
|
||||
"message": "Nothing was imported."
|
||||
"message": "아무것도 가져오지 못했습니다."
|
||||
},
|
||||
"importEncKeyError": {
|
||||
"message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data."
|
||||
@ -3232,7 +3250,7 @@
|
||||
"description": "Label indicating the most common import formats"
|
||||
},
|
||||
"overrideDefaultBrowserAutofillTitle": {
|
||||
"message": "Make Bitwarden your default password manager?",
|
||||
"message": "Bitwarden을 기본 비밀번호 관리자로 지정하시겠습니까?",
|
||||
"description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior"
|
||||
},
|
||||
"overrideDefaultBrowserAutofillDescription": {
|
||||
@ -3240,7 +3258,7 @@
|
||||
"description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior"
|
||||
},
|
||||
"overrideDefaultBrowserAutoFillSettings": {
|
||||
"message": "Make Bitwarden your default password manager",
|
||||
"message": "Bitwarden을 기본 비밀번호 관리자로 지정",
|
||||
"description": "Label for the setting that allows overriding the default browser autofill settings"
|
||||
},
|
||||
"privacyPermissionAdditionNotGrantedTitle": {
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
@ -3391,13 +3402,13 @@
|
||||
"message": "Admin Console"
|
||||
},
|
||||
"accountSecurity": {
|
||||
"message": "Account security"
|
||||
"message": "계정 보안"
|
||||
},
|
||||
"notifications": {
|
||||
"message": "Notifications"
|
||||
"message": "알림"
|
||||
},
|
||||
"appearance": {
|
||||
"message": "Appearance"
|
||||
"message": "화면 스타일"
|
||||
},
|
||||
"errorAssigningTargetCollection": {
|
||||
"message": "Error assigning target collection."
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Tapatybė"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Slaptažodžio istorija"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -390,13 +390,13 @@
|
||||
"message": "Izlasē"
|
||||
},
|
||||
"unfavorite": {
|
||||
"message": "Unfavorite"
|
||||
"message": "Noņemt no izlases"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Vienums pievienots izlasē"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Vienums noņemts no izlases"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Piezīmes"
|
||||
@ -420,7 +420,7 @@
|
||||
"message": "Palaist"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "Launch website"
|
||||
"message": "Atvērt tīmekļvietni"
|
||||
},
|
||||
"website": {
|
||||
"message": "Tīmekļa vietne"
|
||||
@ -612,7 +612,7 @@
|
||||
"message": "Atteicies"
|
||||
},
|
||||
"loggedOutDesc": {
|
||||
"message": "You have been logged out of your account."
|
||||
"message": "Notika izrakstīšanās no Tava konta."
|
||||
},
|
||||
"loginExpired": {
|
||||
"message": "Pieteikšanās sesija ir beigusies."
|
||||
@ -1123,13 +1123,13 @@
|
||||
"message": "Norādīt pašuzstādīta Bitwarden pamata URL."
|
||||
},
|
||||
"selfHostedBaseUrlHint": {
|
||||
"message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com"
|
||||
"message": "Jānorāda sava pašizvietotā Bitward servera pamata URL. Piemērs: https://bitwarden.uznemums.lv"
|
||||
},
|
||||
"selfHostedCustomEnvHeader": {
|
||||
"message": "For advanced configuration, you can specify the base URL of each service independently."
|
||||
"message": "Papildu konfigurācijā ir iespējams norādīt URL katram pakalpojumam atsevišķi."
|
||||
},
|
||||
"selfHostedEnvFormInvalid": {
|
||||
"message": "You must add either the base Server URL or at least one custom environment."
|
||||
"message": "Jāpievieno vai no servera pamata URL vai vismaz viena pielāgota vide."
|
||||
},
|
||||
"customEnvironment": {
|
||||
"message": "Pielāgota vide"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitāte"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Jauns/a $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Labot $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Paroļu vēsture"
|
||||
},
|
||||
@ -1444,7 +1462,7 @@
|
||||
"message": "Krājumi"
|
||||
},
|
||||
"nCollections": {
|
||||
"message": "$COUNT$ collections",
|
||||
"message": "$COUNT$ krājumi",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
@ -1682,7 +1700,7 @@
|
||||
"message": "Automātiski aizpildīt un saglabāt"
|
||||
},
|
||||
"fillAndSave": {
|
||||
"message": "Fill and save"
|
||||
"message": "Aizpildīt un saglabāt"
|
||||
},
|
||||
"autoFillSuccessAndSavedUri": {
|
||||
"message": "Automātiski aizpildīts vienums un saglabāts URI"
|
||||
@ -1781,10 +1799,10 @@
|
||||
"message": "Labi"
|
||||
},
|
||||
"errorRefreshingAccessToken": {
|
||||
"message": "Access Token Refresh Error"
|
||||
"message": "Piekļuves pilnvaras atsvaizināšanas kļūda"
|
||||
},
|
||||
"errorRefreshingAccessTokenDesc": {
|
||||
"message": "No refresh token or API keys found. Please try logging out and logging back in."
|
||||
"message": "Netika atrastas atsvaidzināšanas pilnvaras vai API atslēgas. Lūgums mēģināt izrakstīties un atkal pieteikties."
|
||||
},
|
||||
"desktopSyncVerificationTitle": {
|
||||
"message": "Darbvirsmas sinhronizācijas apstiprinājums"
|
||||
@ -3305,18 +3323,8 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Jānotīra atlases vērtības vai jāmēģina cits meklēšanas vaicājums"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"message": "Ievietot starpliktuvē informāciju - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3325,18 +3333,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"message": "Ievietot starpliktuvē piezīmi - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3346,7 +3344,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsLabel": {
|
||||
"message": "More options, $ITEMNAME$",
|
||||
"message": "Vairāk iespēju, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3356,7 +3354,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsTitle": {
|
||||
"message": "More options - $ITEMNAME$",
|
||||
"message": "Vairāk iespēju - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3366,7 +3364,7 @@
|
||||
}
|
||||
},
|
||||
"viewItemTitle": {
|
||||
"message": "View item - $ITEMNAME$",
|
||||
"message": "Skatīt vienumu - $ITEMNAME$",
|
||||
"description": "Title for a link that opens a view for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3375,17 +3373,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Automātiski aizpildīt - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Nav vērtību, ko ievietot starpliktuvē"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
"message": "Piešķirt krājumus"
|
||||
},
|
||||
"copyEmail": {
|
||||
"message": "Copy email"
|
||||
"message": "Ievietot starpliktuvē e-pasta adresi"
|
||||
},
|
||||
"copyPhone": {
|
||||
"message": "Copy phone"
|
||||
"message": "Ievietot starpliktuvē tālruņa numuru"
|
||||
},
|
||||
"copyAddress": {
|
||||
"message": "Copy address"
|
||||
"message": "Ievietot starpliktuvē adresi"
|
||||
},
|
||||
"adminConsole": {
|
||||
"message": "pārvaldības konsolē,"
|
||||
@ -3439,12 +3450,12 @@
|
||||
}
|
||||
},
|
||||
"itemsWithNoFolder": {
|
||||
"message": "Items with no folder"
|
||||
"message": "Vienumi bez mapes"
|
||||
},
|
||||
"organizationIsDeactivated": {
|
||||
"message": "Organization is deactivated"
|
||||
"message": "Apvienība ir atspējota"
|
||||
},
|
||||
"contactYourOrgAdmin": {
|
||||
"message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance."
|
||||
"message": "Atspējotu apvienību vienumiem nevar piekļūt. Jāsazinās ar apvienības īpašnieku, lai iegūtu palīdzību."
|
||||
}
|
||||
}
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "ഐഡന്റിറ്റി"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "പാസ്സ്വേഡ് നാൾവഴി"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitet"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Passordhistorikk"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identiteit"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Nieuw $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "$TYPE$ bewerken",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Geschiedenis"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Wis filters of probeer een andere zoekterm"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Automatisch invullen - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Geen waarden om te kopiëren"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Collecties toewijzen"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Tożsamość"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Nowy $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edytuj $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Historia hasła"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Wyczyść filtry lub użyj innej frazy"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Skopiuj informacje, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Skopiuj informacje - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Skopiuj notatkę, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Skopiuj notatkę - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Autouzupełnij - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Brak wartości do skopiowania"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Przypisz kolekcje"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identidade"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Histórico de Senha"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Limpar filtros ou tentar outro termo de pesquisa"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identidade"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Novo(a) $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Editar $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Histórico de palavras-passe"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Limpe os filtros ou tente outro termo de pesquisa"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copiar informações, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copiar informações - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copiar nota, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copiar nota - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Preencher automaticamente - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Não há valores a copiar"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Atribuir coleções"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitate"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Istoric parole"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Удалить из избранного"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Элемент добавлен в избранное"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Элемент удален из избранного"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Заметки"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Личная информация"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Новый $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Изменить $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "История паролей"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Очистите фильтры или попробуйте другой поисковый запрос"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Скопировать информацию, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Скопировать информацию - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Скопировать заметку, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Скопировать заметку - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Автозаполнение - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Нет значений для копирования"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Назначить коллекции"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "අනන්යතාවය"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "මුරපද ඉතිහාසය"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Odstrániť z obľúbených"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Položka pridaná medzi obľúbené"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Položka odobraná z obľúbených"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Poznámky"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identita"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Nové $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Upraviť $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "História hesla"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Vymažte filtre alebo zmeňte vyhľadávaný výraz"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Skopírovať info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Skopírovať info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Skopírovať poznámku, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Skopírovať poznámku - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Automatické vyplnenie - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Nie je čo kopírovať"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Prideliť zbierky"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identiteta"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Zgodovina gesel"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Идентитет"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Историја Лозинке"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Обришите филтере или покушајте са другим термином"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Копирај информације, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Копирај информације - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Копирај Белешку, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Копирај Белешку - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Додели колекције"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identitet"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Ny $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Redigera $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Lösenordshistorik"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Tilldela samlingar"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Identity"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Password history"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "ข้อมูลระบุตัวตน"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "ประวัติของรหัสผ่าน"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Kimlik"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Yeni $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Parola geçmişi"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Filtreleri temizleyin veya başka bir arama yapmayı deneyin"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Bilgileri kopyala, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Bilgileri kopyala - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Notu kopyala, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Notu kopyala - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Otomatik doldur - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Kopyalanacak değer yok"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Koleksiyon ata"
|
||||
},
|
||||
|
@ -393,10 +393,10 @@
|
||||
"message": "Вилучити з обраного"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "Запис додано до обраного"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "Запис вилучено з обраного"
|
||||
},
|
||||
"notes": {
|
||||
"message": "Нотатки"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Особисті дані"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Новий $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Редагувати $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Історія паролів"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Скиньте фільтри або спробуйте іншу умову пошуку"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Копіювати інформацію, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Копіювати інформацію – $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Копіювати нотатку, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Копіювати нотатку – $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Автозаповнення – $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "Немає значень для копіювання"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Призначити збірки"
|
||||
},
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "Danh tính"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "Lịch sử mật khẩu"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -390,13 +390,13 @@
|
||||
"message": "收藏"
|
||||
},
|
||||
"unfavorite": {
|
||||
"message": "Unfavorite"
|
||||
"message": "取消收藏"
|
||||
},
|
||||
"itemAddedToFavorites": {
|
||||
"message": "Item added to favorites"
|
||||
"message": "项目已添加到收藏夹"
|
||||
},
|
||||
"itemRemovedFromFavorites": {
|
||||
"message": "Item removed from favorites"
|
||||
"message": "项目已移出收藏夹"
|
||||
},
|
||||
"notes": {
|
||||
"message": "备注"
|
||||
@ -420,7 +420,7 @@
|
||||
"message": "前往"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "Launch website"
|
||||
"message": "启动网站"
|
||||
},
|
||||
"website": {
|
||||
"message": "网站"
|
||||
@ -612,7 +612,7 @@
|
||||
"message": "已注销"
|
||||
},
|
||||
"loggedOutDesc": {
|
||||
"message": "You have been logged out of your account."
|
||||
"message": "您已注销您的账户。"
|
||||
},
|
||||
"loginExpired": {
|
||||
"message": "您的登录会话已过期。"
|
||||
@ -1123,13 +1123,13 @@
|
||||
"message": "指定您本地托管的 Bitwarden 安装的基础 URL。"
|
||||
},
|
||||
"selfHostedBaseUrlHint": {
|
||||
"message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com"
|
||||
"message": "指定您的本地托管 Bitwarden 安装的基础 URL。例如:https://bitwarden.company.com"
|
||||
},
|
||||
"selfHostedCustomEnvHeader": {
|
||||
"message": "For advanced configuration, you can specify the base URL of each service independently."
|
||||
"message": "对于高级配置,您可以单独指定每个服务的基础 URL。"
|
||||
},
|
||||
"selfHostedEnvFormInvalid": {
|
||||
"message": "You must add either the base Server URL or at least one custom environment."
|
||||
"message": "您必须添加基础服务器 URL 或至少添加一个自定义环境。"
|
||||
},
|
||||
"customEnvironment": {
|
||||
"message": "自定义环境"
|
||||
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "身份"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "新增 $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "编辑 $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "密码历史记录"
|
||||
},
|
||||
@ -1444,7 +1462,7 @@
|
||||
"message": "集合"
|
||||
},
|
||||
"nCollections": {
|
||||
"message": "$COUNT$ collections",
|
||||
"message": "$COUNT$ 个集合",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
@ -1682,7 +1700,7 @@
|
||||
"message": "自动填充并保存"
|
||||
},
|
||||
"fillAndSave": {
|
||||
"message": "Fill and save"
|
||||
"message": "填充并保存"
|
||||
},
|
||||
"autoFillSuccessAndSavedUri": {
|
||||
"message": "项目已自动填充且 URI 已保存"
|
||||
@ -1781,10 +1799,10 @@
|
||||
"message": "确定"
|
||||
},
|
||||
"errorRefreshingAccessToken": {
|
||||
"message": "Access Token Refresh Error"
|
||||
"message": "访问令牌刷新错误"
|
||||
},
|
||||
"errorRefreshingAccessTokenDesc": {
|
||||
"message": "No refresh token or API keys found. Please try logging out and logging back in."
|
||||
"message": "未找到刷新令牌或 API 密钥。请尝试注销然后重新登录。"
|
||||
},
|
||||
"desktopSyncVerificationTitle": {
|
||||
"message": "桌面同步验证"
|
||||
@ -2675,7 +2693,7 @@
|
||||
"message": "批准后,您将收到通知。"
|
||||
},
|
||||
"troubleLoggingIn": {
|
||||
"message": "登录遇到问题?"
|
||||
"message": "登录遇到问题吗?"
|
||||
},
|
||||
"loginApproved": {
|
||||
"message": "登录已批准"
|
||||
@ -3305,18 +3323,8 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "清除筛选器或尝试另一个搜索词"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@ -3325,18 +3333,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"message": "复制备注 - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3346,7 +3344,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": {
|
||||
@ -3356,7 +3354,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": {
|
||||
@ -3366,7 +3364,7 @@
|
||||
}
|
||||
},
|
||||
"viewItemTitle": {
|
||||
"message": "View item - $ITEMNAME$",
|
||||
"message": "查看项目 - $ITEMNAME$",
|
||||
"description": "Title for a link that opens a view for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@ -3375,17 +3373,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "自动填充 - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "没有要复制的值"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
"message": "分配集合"
|
||||
},
|
||||
"copyEmail": {
|
||||
"message": "Copy email"
|
||||
"message": "复制电子邮件地址"
|
||||
},
|
||||
"copyPhone": {
|
||||
"message": "Copy phone"
|
||||
"message": "复制电话号码"
|
||||
},
|
||||
"copyAddress": {
|
||||
"message": "Copy address"
|
||||
"message": "复制地址"
|
||||
},
|
||||
"adminConsole": {
|
||||
"message": "管理控制台"
|
||||
@ -3426,7 +3437,7 @@
|
||||
}
|
||||
},
|
||||
"new": {
|
||||
"message": "新建"
|
||||
"message": "新增"
|
||||
},
|
||||
"removeItem": {
|
||||
"message": "删除 $NAME$",
|
||||
@ -3439,12 +3450,12 @@
|
||||
}
|
||||
},
|
||||
"itemsWithNoFolder": {
|
||||
"message": "Items with no folder"
|
||||
"message": "无文件夹的项目"
|
||||
},
|
||||
"organizationIsDeactivated": {
|
||||
"message": "组织已停用"
|
||||
},
|
||||
"contactYourOrgAdmin": {
|
||||
"message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance."
|
||||
"message": "无法访问已停用组织中的项目。请联系您的组织所有者获取协助。"
|
||||
}
|
||||
}
|
||||
|
@ -1434,6 +1434,24 @@
|
||||
"typeIdentity": {
|
||||
"message": "身分"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "New $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editItemHeader": {
|
||||
"message": "Edit $TYPE$",
|
||||
"placeholders": {
|
||||
"type": {
|
||||
"content": "$1",
|
||||
"example": "Login"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passwordHistory": {
|
||||
"message": "密碼歷史記錄"
|
||||
},
|
||||
@ -3305,16 +3323,6 @@
|
||||
"clearFiltersOrTryAnother": {
|
||||
"message": "Clear filters or try another search term"
|
||||
},
|
||||
"copyInfoLabel": {
|
||||
"message": "Copy info, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
@ -3325,16 +3333,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteLabel": {
|
||||
"message": "Copy Note, $ITEMNAME$",
|
||||
"description": "Aria label for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Note Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
@ -3375,6 +3373,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"autofillTitle": {
|
||||
"message": "Auto-fill - $ITEMNAME$",
|
||||
"description": "Title for a button that auto-fills a login item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
"example": "Secret Item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noValuesToCopy": {
|
||||
"message": "No values to copy"
|
||||
},
|
||||
"assignCollections": {
|
||||
"message": "Assign collections"
|
||||
},
|
||||
|
@ -30,7 +30,9 @@
|
||||
</form>
|
||||
<p class="createAccountLink">
|
||||
{{ "newAroundHere" | i18n }}
|
||||
<a routerLink="/register" (click)="setLoginEmailValues()">{{ "createAccount" | i18n }}</a>
|
||||
<a [routerLink]="registerRoute" (click)="setLoginEmailValues()">{{
|
||||
"createAccount" | i18n
|
||||
}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,6 +5,8 @@ import { Subject, firstValueFrom, takeUntil } from "rxjs";
|
||||
|
||||
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
|
||||
import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -26,6 +28,9 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
rememberEmail: [false],
|
||||
});
|
||||
|
||||
// TODO: remove when email verification flag is removed
|
||||
registerRoute = "/register";
|
||||
|
||||
constructor(
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private formBuilder: FormBuilder,
|
||||
@ -34,9 +39,19 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
private environmentService: EnvironmentService,
|
||||
private loginEmailService: LoginEmailServiceAbstraction,
|
||||
private accountSwitcherService: AccountSwitcherService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
// TODO: remove when email verification flag is removed
|
||||
const emailVerification = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.EmailVerification,
|
||||
);
|
||||
|
||||
if (emailVerification) {
|
||||
this.registerRoute = "/signup";
|
||||
}
|
||||
|
||||
const email = this.loginEmailService.getEmail();
|
||||
const rememberEmail = this.loginEmailService.getRememberEmail();
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
<div id="login-initiated">
|
||||
<header>
|
||||
<h1 class="margin-auto">
|
||||
<app-header>
|
||||
<div class="left">
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "loginInitiated" | i18n }}</span>
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<div class="right"></div>
|
||||
</app-header>
|
||||
<div class="content login-page">
|
||||
<div class="full-loading-spinner" *ngIf="loading">
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
|
||||
|
@ -13,6 +13,7 @@ import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstraction
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -51,6 +52,7 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
ssoLoginService: SsoLoginServiceAbstraction,
|
||||
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
|
||||
configService: ConfigService,
|
||||
) {
|
||||
super(
|
||||
devicesApiService,
|
||||
@ -71,6 +73,7 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
loginEmailService,
|
||||
ssoLoginService,
|
||||
webAuthnLoginService,
|
||||
configService,
|
||||
);
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
|
@ -1,12 +1,16 @@
|
||||
import { AutofillInlineMenuContentService } from "../overlay/inline-menu/content/autofill-inline-menu-content.service";
|
||||
import { AutofillOverlayContentService } from "../services/autofill-overlay-content.service";
|
||||
import { InlineMenuFieldQualificationService } from "../services/inline-menu-field-qualification.service";
|
||||
import { setupAutofillInitDisconnectAction } from "../utils";
|
||||
|
||||
import AutofillInit from "./autofill-init";
|
||||
|
||||
(function (windowContext) {
|
||||
if (!windowContext.bitwardenAutofillInit) {
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService();
|
||||
const inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
let inlineMenuElements: AutofillInlineMenuContentService;
|
||||
if (globalThis.self === globalThis.top) {
|
||||
inlineMenuElements = new AutofillInlineMenuContentService();
|
||||
|
@ -3,6 +3,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { SubFrameOffsetData } from "../../background/abstractions/overlay.background";
|
||||
import { AutofillExtensionMessageParam } from "../../content/abstractions/autofill-init";
|
||||
import AutofillField from "../../models/autofill-field";
|
||||
import AutofillPageDetails from "../../models/autofill-page-details";
|
||||
import { ElementWithOpId, FormFieldElement } from "../../types";
|
||||
|
||||
export type OpenAutofillInlineMenuOptions = {
|
||||
@ -36,9 +37,10 @@ export interface AutofillOverlayContentService {
|
||||
pageDetailsUpdateRequired: boolean;
|
||||
messageHandlers: AutofillOverlayContentExtensionMessageHandlers;
|
||||
init(): void;
|
||||
setupInlineMenuListenerOnField(
|
||||
setupInlineMenu(
|
||||
autofillFieldElement: ElementWithOpId<FormFieldElement>,
|
||||
autofillFieldData: AutofillField,
|
||||
pageDetails: AutofillPageDetails,
|
||||
): Promise<void>;
|
||||
blurMostRecentlyFocusedField(isClosingInlineMenu?: boolean): void;
|
||||
destroy(): void;
|
||||
|
@ -0,0 +1,6 @@
|
||||
import AutofillField from "../../models/autofill-field";
|
||||
import AutofillPageDetails from "../../models/autofill-page-details";
|
||||
|
||||
export interface InlineMenuFieldQualificationService {
|
||||
isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean;
|
||||
}
|
@ -10,17 +10,21 @@ import {
|
||||
RedirectFocusDirection,
|
||||
} from "../enums/autofill-overlay.enum";
|
||||
import AutofillField from "../models/autofill-field";
|
||||
import AutofillForm from "../models/autofill-form";
|
||||
import AutofillPageDetails from "../models/autofill-page-details";
|
||||
import { createAutofillFieldMock } from "../spec/autofill-mocks";
|
||||
import { flushPromises, postWindowMessage, sendMockExtensionMessage } from "../spec/testing-utils";
|
||||
import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types";
|
||||
|
||||
import { AutoFillConstants } from "./autofill-constants";
|
||||
import { AutofillOverlayContentService } from "./autofill-overlay-content.service";
|
||||
import { InlineMenuFieldQualificationService } from "./inline-menu-field-qualification.service";
|
||||
|
||||
const defaultWindowReadyState = document.readyState;
|
||||
const defaultDocumentVisibilityState = document.visibilityState;
|
||||
describe("AutofillOverlayContentService", () => {
|
||||
let autofillInit: AutofillInit;
|
||||
let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
|
||||
let autofillOverlayContentService: AutofillOverlayContentService;
|
||||
let sendExtensionMessageSpy: jest.SpyInstance;
|
||||
const sendResponseSpy = jest.fn();
|
||||
@ -35,7 +39,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
autofillOverlayContentService = new AutofillOverlayContentService();
|
||||
inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||
autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
autofillInit = new AutofillInit(autofillOverlayContentService);
|
||||
autofillInit.init();
|
||||
sendExtensionMessageSpy = jest
|
||||
@ -120,9 +127,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("setupInlineMenuListenerOnField", () => {
|
||||
describe("setupInlineMenu", () => {
|
||||
let autofillFieldElement: ElementWithOpId<FormFieldElement>;
|
||||
let autofillFieldData: AutofillField;
|
||||
let pageDetailsMock: AutofillPageDetails;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
@ -144,87 +152,52 @@ describe("AutofillOverlayContentService", () => {
|
||||
placeholder: "username",
|
||||
elementNumber: 1,
|
||||
});
|
||||
const passwordFieldData = createAutofillFieldMock({
|
||||
opid: "password-field",
|
||||
form: "validFormId",
|
||||
elementNumber: 2,
|
||||
autocompleteType: "current-password",
|
||||
type: "password",
|
||||
});
|
||||
pageDetailsMock = mock<AutofillPageDetails>({
|
||||
forms: { validFormId: mock<AutofillForm>() },
|
||||
fields: [autofillFieldData, passwordFieldData],
|
||||
});
|
||||
});
|
||||
|
||||
describe("skips setup for ignored form fields", () => {
|
||||
beforeEach(() => {
|
||||
autofillFieldData = mock<AutofillField>();
|
||||
});
|
||||
|
||||
it("ignores fields that are readonly", async () => {
|
||||
autofillFieldData.readonly = true;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores fields that contain a disabled attribute", async () => {
|
||||
autofillFieldData.disabled = true;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores fields that are not viewable", async () => {
|
||||
autofillFieldData.viewable = false;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
autofillFieldData = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlName: "username",
|
||||
htmlID: "username",
|
||||
placeholder: "username",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores fields that are part of the ExcludedInlineMenuTypes", () => {
|
||||
AutoFillConstants.ExcludedInlineMenuTypes.forEach(async (excludedType) => {
|
||||
autofillFieldData.type = excludedType;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores fields that contain the keyword `search`", async () => {
|
||||
autofillFieldData.placeholder = "search";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores fields that contain the keyword `captcha` ", async () => {
|
||||
autofillFieldData.placeholder = "captcha";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores fields that do not appear as a login field", async () => {
|
||||
autofillFieldData.htmlName = "another-type-of-field";
|
||||
autofillFieldData.htmlID = "another-type-of-field";
|
||||
autofillFieldData.placeholder = "another-type-of-field";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
@ -234,9 +207,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
it("skips setup on fields that have been previously set up", async () => {
|
||||
autofillOverlayContentService["formFieldElements"].add(autofillFieldElement);
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
@ -247,9 +221,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
sendExtensionMessageSpy.mockResolvedValueOnce(undefined);
|
||||
autofillOverlayContentService["inlineMenuVisibility"] = undefined;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("getAutofillInlineMenuVisibility");
|
||||
@ -262,9 +237,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
sendExtensionMessageSpy.mockResolvedValueOnce(AutofillOverlayVisibility.OnFieldFocus);
|
||||
autofillOverlayContentService["inlineMenuVisibility"] = undefined;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(autofillOverlayContentService["inlineMenuVisibility"]).toEqual(
|
||||
@ -285,9 +261,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
"op-1-username-field-focus-handler": focusHandler,
|
||||
};
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(autofillFieldElement.removeEventListener).toHaveBeenNthCalledWith(
|
||||
@ -314,9 +291,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
|
||||
describe("form field blur event listener", () => {
|
||||
beforeEach(async () => {
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
});
|
||||
|
||||
@ -337,9 +315,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
|
||||
describe("form field keyup event listener", () => {
|
||||
beforeEach(async () => {
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
jest.spyOn(globalThis.customElements, "define").mockImplementation();
|
||||
});
|
||||
@ -433,9 +412,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
) as ElementWithOpId<HTMLSpanElement>;
|
||||
jest.spyOn(autofillOverlayContentService as any, "storeModifiedFormElement");
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
spanAutofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
spanAutofillFieldElement.dispatchEvent(new Event("input"));
|
||||
@ -447,9 +427,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] =
|
||||
mock<ElementWithOpId<FormFieldElement>>();
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
@ -461,9 +442,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
});
|
||||
|
||||
it("stores the field as a user filled field if the form field data indicates that it is for a username", async () => {
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
|
||||
@ -477,9 +459,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
"password-field",
|
||||
) as ElementWithOpId<FormFieldElement>;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
passwordFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
passwordFieldElement.dispatchEvent(new Event("input"));
|
||||
|
||||
@ -492,9 +475,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "isUserAuthed").mockReturnValue(false);
|
||||
(autofillFieldElement as HTMLInputElement).value = "test";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
await flushPromises();
|
||||
@ -513,9 +497,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
|
||||
(autofillFieldElement as HTMLInputElement).value = "test";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
await flushPromises();
|
||||
@ -530,9 +515,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "openInlineMenu");
|
||||
(autofillFieldElement as HTMLInputElement).value = "";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
await flushPromises();
|
||||
@ -545,9 +531,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "openInlineMenu");
|
||||
(autofillFieldElement as HTMLInputElement).value = "";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
await flushPromises();
|
||||
@ -563,9 +550,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "openInlineMenu");
|
||||
(autofillFieldElement as HTMLInputElement).value = "";
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillFieldElement.dispatchEvent(new Event("input"));
|
||||
await flushPromises();
|
||||
@ -579,9 +567,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
jest
|
||||
.spyOn(autofillOverlayContentService as any, "triggerFormFieldFocusedAction")
|
||||
.mockImplementation();
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
});
|
||||
|
||||
@ -635,9 +624,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement;
|
||||
autofillOverlayContentService["inlineMenuVisibility"] =
|
||||
AutofillOverlayVisibility.OnFieldFocus;
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -647,9 +637,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
});
|
||||
|
||||
it("updates the most recently focused field", async () => {
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -664,9 +655,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
it("removes the overlay list if the autofill visibility is set to onClick", async () => {
|
||||
autofillOverlayContentService["inlineMenuVisibility"] =
|
||||
AutofillOverlayVisibility.OnButtonClick;
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -683,9 +675,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
"input",
|
||||
) as ElementWithOpId<HTMLInputElement>;
|
||||
(autofillFieldElement as HTMLInputElement).value = "test";
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -701,9 +694,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
(autofillFieldElement as HTMLInputElement).value = "";
|
||||
autofillOverlayContentService["inlineMenuVisibility"] =
|
||||
AutofillOverlayVisibility.OnFieldFocus;
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -717,9 +711,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
autofillOverlayContentService["inlineMenuVisibility"] =
|
||||
AutofillOverlayVisibility.OnFieldFocus;
|
||||
jest.spyOn(autofillOverlayContentService as any, "isUserAuthed").mockReturnValue(true);
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -735,9 +730,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
jest
|
||||
.spyOn(autofillOverlayContentService as any, "isInlineMenuCiphersPopulated")
|
||||
.mockReturnValue(true);
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -752,9 +748,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
describe("hidden form field focus event", () => {
|
||||
it("sets up the inline menu listeners if the autofill field data is in the cache", async () => {
|
||||
autofillFieldData.viewable = false;
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
autofillFieldElement.dispatchEvent(new Event("focus"));
|
||||
@ -785,9 +782,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
|
||||
it("skips setting up the inline menu listeners if the autofill field data is not in the cache", async () => {
|
||||
autofillFieldData.viewable = false;
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillOverlayContentService["formFieldElements"].delete(autofillFieldElement);
|
||||
|
||||
@ -821,9 +819,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
writable: true,
|
||||
});
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("openAutofillInlineMenu");
|
||||
@ -835,9 +834,10 @@ describe("AutofillOverlayContentService", () => {
|
||||
it("sets the most recently focused field to the passed form field element if the value is not set", async () => {
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] = undefined;
|
||||
|
||||
await autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
await autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
|
||||
expect(autofillOverlayContentService["mostRecentlyFocusedField"]).toEqual(
|
||||
@ -1608,8 +1608,8 @@ describe("AutofillOverlayContentService", () => {
|
||||
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("updateSubFrameData", {
|
||||
subFrameData: {
|
||||
frameId: 10,
|
||||
left: 168,
|
||||
top: 168,
|
||||
left: expect.any(Number),
|
||||
top: expect.any(Number),
|
||||
url: "https://example.com/",
|
||||
parentFrameIds: [1, 2, 3, 4],
|
||||
subFrameDepth: expect.any(Number),
|
||||
@ -1652,6 +1652,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
describe("destroy", () => {
|
||||
let autofillFieldElement: ElementWithOpId<FormFieldElement>;
|
||||
let autofillFieldData: AutofillField;
|
||||
let pageDetailsMock: AutofillPageDetails;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
@ -1671,11 +1672,21 @@ describe("AutofillOverlayContentService", () => {
|
||||
placeholder: "username",
|
||||
elementNumber: 1,
|
||||
});
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
const passwordFieldData = createAutofillFieldMock({
|
||||
opid: "password-field",
|
||||
form: "validFormId",
|
||||
elementNumber: 2,
|
||||
autocompleteType: "current-password",
|
||||
type: "password",
|
||||
});
|
||||
pageDetailsMock = mock<AutofillPageDetails>({
|
||||
forms: { validFormId: mock<AutofillForm>() },
|
||||
fields: [autofillFieldData, passwordFieldData],
|
||||
});
|
||||
void autofillOverlayContentService.setupInlineMenu(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
pageDetailsMock,
|
||||
);
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement;
|
||||
});
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
RedirectFocusDirection,
|
||||
} from "../enums/autofill-overlay.enum";
|
||||
import AutofillField from "../models/autofill-field";
|
||||
import AutofillPageDetails from "../models/autofill-page-details";
|
||||
import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types";
|
||||
import {
|
||||
elementIsFillableFormField,
|
||||
@ -30,6 +31,7 @@ import {
|
||||
OpenAutofillInlineMenuOptions,
|
||||
SubFrameDataFromWindowMessage,
|
||||
} from "./abstractions/autofill-overlay-content.service";
|
||||
import { InlineMenuFieldQualificationService } from "./abstractions/inline-menu-field-qualifications.service";
|
||||
import { AutoFillConstants } from "./autofill-constants";
|
||||
|
||||
export class AutofillOverlayContentService implements AutofillOverlayContentServiceInterface {
|
||||
@ -50,7 +52,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
private recalculateSubFrameOffsetsTimeout: number | NodeJS.Timeout;
|
||||
private reflowPerformanceObserver: PerformanceObserver;
|
||||
private reflowMutationObserver: MutationObserver;
|
||||
private autofillFieldKeywordsMap: WeakMap<AutofillField, string> = new WeakMap();
|
||||
private eventHandlersMemo: { [key: string]: EventListener } = {};
|
||||
private readonly extensionMessageHandlers: AutofillOverlayContentExtensionMessageHandlers = {
|
||||
openAutofillInlineMenu: ({ message }) => this.openInlineMenu(message),
|
||||
@ -70,6 +71,8 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
destroyAutofillInlineMenuListeners: () => this.destroy(),
|
||||
};
|
||||
|
||||
constructor(private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService) {}
|
||||
|
||||
/**
|
||||
* Initializes the autofill overlay content service by setting up the mutation observers.
|
||||
* The observers will be instantiated on DOMContentLoaded if the page is current loading.
|
||||
@ -97,12 +100,17 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
*
|
||||
* @param formFieldElement - Form field elements identified during the page details collection process.
|
||||
* @param autofillFieldData - Autofill field data captured from the form field element.
|
||||
* @param pageDetails - The collected page details from the tab.
|
||||
*/
|
||||
async setupInlineMenuListenerOnField(
|
||||
async setupInlineMenu(
|
||||
formFieldElement: ElementWithOpId<FormFieldElement>,
|
||||
autofillFieldData: AutofillField,
|
||||
pageDetails: AutofillPageDetails,
|
||||
) {
|
||||
if (this.isIgnoredField(autofillFieldData) || this.formFieldElements.has(formFieldElement)) {
|
||||
if (
|
||||
this.formFieldElements.has(formFieldElement) ||
|
||||
this.isIgnoredField(autofillFieldData, pageDetails)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,6 +118,12 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
return;
|
||||
}
|
||||
|
||||
await this.setupInlineMenuOnQualifiedField(formFieldElement);
|
||||
}
|
||||
|
||||
private async setupInlineMenuOnQualifiedField(
|
||||
formFieldElement: ElementWithOpId<FormFieldElement>,
|
||||
) {
|
||||
this.formFieldElements.add(formFieldElement);
|
||||
|
||||
if (!this.mostRecentlyFocusedField) {
|
||||
@ -517,51 +531,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
return this.authStatus === AuthenticationStatus.Unlocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if the autofill field's data contains any of
|
||||
* the keyboards matching the passed list of keywords.
|
||||
*
|
||||
* @param autofillFieldData - Autofill field data captured from the form field element.
|
||||
* @param keywords - Keywords to search for in the autofill field data.
|
||||
*/
|
||||
private keywordsFoundInFieldData(autofillFieldData: AutofillField, keywords: string[]) {
|
||||
const searchedString = this.getAutofillFieldDataKeywords(autofillFieldData);
|
||||
return keywords.some((keyword) => searchedString.includes(keyword));
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates the autofill field's data into a single string
|
||||
* that can be used to search for keywords.
|
||||
*
|
||||
* @param autofillFieldData - Autofill field data captured from the form field element.
|
||||
*/
|
||||
private getAutofillFieldDataKeywords(autofillFieldData: AutofillField) {
|
||||
if (this.autofillFieldKeywordsMap.has(autofillFieldData)) {
|
||||
return this.autofillFieldKeywordsMap.get(autofillFieldData);
|
||||
}
|
||||
|
||||
const keywordValues = [
|
||||
autofillFieldData.htmlID,
|
||||
autofillFieldData.htmlName,
|
||||
autofillFieldData.htmlClass,
|
||||
autofillFieldData.type,
|
||||
autofillFieldData.title,
|
||||
autofillFieldData.placeholder,
|
||||
autofillFieldData.autoCompleteType,
|
||||
autofillFieldData["label-data"],
|
||||
autofillFieldData["label-aria"],
|
||||
autofillFieldData["label-left"],
|
||||
autofillFieldData["label-right"],
|
||||
autofillFieldData["label-tag"],
|
||||
autofillFieldData["label-top"],
|
||||
]
|
||||
.join(",")
|
||||
.toLowerCase();
|
||||
this.autofillFieldKeywordsMap.set(autofillFieldData, keywordValues);
|
||||
|
||||
return keywordValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the most recently focused field is currently
|
||||
* focused within the root node relative to the field.
|
||||
@ -698,20 +667,20 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
* updated in the future to support other types of forms.
|
||||
*
|
||||
* @param autofillFieldData - Autofill field data captured from the form field element.
|
||||
* @param pageDetails - The collected page details from the tab.
|
||||
*/
|
||||
private isIgnoredField(autofillFieldData: AutofillField): boolean {
|
||||
if (
|
||||
this.ignoredFieldTypes.has(autofillFieldData.type) ||
|
||||
this.keywordsFoundInFieldData(autofillFieldData, ["search", "captcha"])
|
||||
) {
|
||||
private isIgnoredField(
|
||||
autofillFieldData: AutofillField,
|
||||
pageDetails: AutofillPageDetails,
|
||||
): boolean {
|
||||
if (this.ignoredFieldTypes.has(autofillFieldData.type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isLoginCipherField =
|
||||
autofillFieldData.type === "password" ||
|
||||
this.keywordsFoundInFieldData(autofillFieldData, AutoFillConstants.UsernameFieldNames);
|
||||
|
||||
return !isLoginCipherField;
|
||||
return !this.inlineMenuFieldQualificationService.isFieldForLoginForm(
|
||||
autofillFieldData,
|
||||
pageDetails,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -732,7 +701,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
}
|
||||
|
||||
this.setupHiddenFieldFallbackListener(formFieldElement, autofillFieldData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -775,7 +743,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
||||
autofillFieldData.readonly = getAttributeBoolean(formFieldElement, "disabled");
|
||||
autofillFieldData.disabled = getAttributeBoolean(formFieldElement, "disabled");
|
||||
autofillFieldData.viewable = true;
|
||||
void this.setupInlineMenuListenerOnField(formFieldElement, autofillFieldData);
|
||||
void this.setupInlineMenuOnQualifiedField(formFieldElement);
|
||||
}
|
||||
|
||||
this.removeHiddenFieldFallbackListener(formFieldElement);
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
FormElementWithAttribute,
|
||||
} from "../types";
|
||||
|
||||
import { InlineMenuFieldQualificationService } from "./abstractions/inline-menu-field-qualifications.service";
|
||||
import { AutofillOverlayContentService } from "./autofill-overlay-content.service";
|
||||
import CollectAutofillContentService from "./collect-autofill-content.service";
|
||||
import DomElementVisibilityService from "./dom-element-visibility.service";
|
||||
@ -28,13 +29,17 @@ const waitForIdleCallback = () => new Promise((resolve) => globalThis.requestIdl
|
||||
|
||||
describe("CollectAutofillContentService", () => {
|
||||
const domElementVisibilityService = new DomElementVisibilityService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService();
|
||||
const inlineMenuFieldQualificationService = mock<InlineMenuFieldQualificationService>();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
let collectAutofillContentService: CollectAutofillContentService;
|
||||
const mockIntersectionObserver = mock<IntersectionObserver>();
|
||||
const mockQuerySelectorAll = mockQuerySelectorAllDefinedCall();
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.requestIdleCallback = jest.fn((cb, options) => setTimeout(cb, 100));
|
||||
globalThis.cancelIdleCallback = jest.fn((id) => clearTimeout(id));
|
||||
document.body.innerHTML = mockLoginForm;
|
||||
collectAutofillContentService = new CollectAutofillContentService(
|
||||
domElementVisibilityService,
|
||||
@ -247,11 +252,16 @@ describe("CollectAutofillContentService", () => {
|
||||
const isFormFieldViewableSpy = jest
|
||||
.spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable")
|
||||
.mockResolvedValue(true);
|
||||
const setupAutofillOverlayListenerOnFieldSpy = jest.spyOn(
|
||||
collectAutofillContentService["autofillOverlayContentService"],
|
||||
"setupInlineMenu",
|
||||
);
|
||||
|
||||
await collectAutofillContentService.getPageDetails();
|
||||
|
||||
expect(autofillField.viewable).toBe(true);
|
||||
expect(isFormFieldViewableSpy).toHaveBeenCalledWith(fieldElement);
|
||||
expect(setupAutofillOverlayListenerOnFieldSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns an object containing information about the current page as well as autofill data for the forms and fields of the page", async () => {
|
||||
@ -1191,7 +1201,7 @@ describe("CollectAutofillContentService", () => {
|
||||
"aria-disabled": false,
|
||||
"aria-haspopup": false,
|
||||
"aria-hidden": false,
|
||||
autoCompleteType: null,
|
||||
autoCompleteType: "off",
|
||||
checked: false,
|
||||
"data-stripe": hiddenField.dataStripe,
|
||||
disabled: false,
|
||||
@ -2558,7 +2568,7 @@ describe("CollectAutofillContentService", () => {
|
||||
);
|
||||
setupAutofillOverlayListenerOnFieldSpy = jest.spyOn(
|
||||
collectAutofillContentService["autofillOverlayContentService"],
|
||||
"setupInlineMenuListenerOnField",
|
||||
"setupInlineMenu",
|
||||
);
|
||||
});
|
||||
|
||||
@ -2622,22 +2632,20 @@ describe("CollectAutofillContentService", () => {
|
||||
expect(setupAutofillOverlayListenerOnFieldSpy).toHaveBeenCalledWith(
|
||||
formFieldElement,
|
||||
autofillField,
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("destroy", () => {
|
||||
it("clears the updateAutofillElementsAfterMutationTimeout", () => {
|
||||
it("clears the updateAfterMutationIdleCallback", () => {
|
||||
jest.spyOn(window, "clearTimeout");
|
||||
collectAutofillContentService["updateAutofillElementsAfterMutationTimeout"] = setTimeout(
|
||||
jest.fn,
|
||||
100,
|
||||
);
|
||||
collectAutofillContentService["updateAfterMutationIdleCallback"] = setTimeout(jest.fn, 100);
|
||||
|
||||
collectAutofillContentService.destroy();
|
||||
|
||||
expect(clearTimeout).toHaveBeenCalledWith(
|
||||
collectAutofillContentService["updateAutofillElementsAfterMutationTimeout"],
|
||||
collectAutofillContentService["updateAfterMutationIdleCallback"],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -7,26 +7,27 @@ import {
|
||||
elementIsDescriptionTermElement,
|
||||
elementIsFillableFormField,
|
||||
elementIsFormElement,
|
||||
elementIsInputElement,
|
||||
elementIsLabelElement,
|
||||
elementIsSelectElement,
|
||||
elementIsSpanElement,
|
||||
nodeIsElement,
|
||||
elementIsInputElement,
|
||||
elementIsTextAreaElement,
|
||||
nodeIsFormElement,
|
||||
nodeIsInputElement,
|
||||
sendExtensionMessage,
|
||||
// sendExtensionMessage,
|
||||
getAttributeBoolean,
|
||||
getPropertyOrAttribute,
|
||||
requestIdleCallbackPolyfill,
|
||||
cancelIdleCallbackPolyfill,
|
||||
} from "../utils";
|
||||
|
||||
import { AutofillOverlayContentService } from "./abstractions/autofill-overlay-content.service";
|
||||
import {
|
||||
UpdateAutofillDataAttributeParams,
|
||||
AutofillFieldElements,
|
||||
AutofillFormElements,
|
||||
CollectAutofillContentService as CollectAutofillContentServiceInterface,
|
||||
UpdateAutofillDataAttributeParams,
|
||||
} from "./abstractions/collect-autofill-content.service";
|
||||
import { DomElementVisibilityService } from "./abstractions/dom-element-visibility.service";
|
||||
|
||||
@ -43,9 +44,9 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
private intersectionObserver: IntersectionObserver;
|
||||
private elementInitializingIntersectionObserver: Set<Element> = new Set();
|
||||
private mutationObserver: MutationObserver;
|
||||
private updateAutofillElementsAfterMutationTimeout: number | NodeJS.Timeout;
|
||||
private mutationsQueue: MutationRecord[][] = [];
|
||||
private readonly updateAfterMutationTimeoutDelay = 1000;
|
||||
private updateAfterMutationIdleCallback: NodeJS.Timeout | number;
|
||||
private readonly updateAfterMutationTimeout = 1000;
|
||||
private readonly formFieldQueryString;
|
||||
private readonly nonInputFormFieldTags = new Set(["textarea", "select"]);
|
||||
private readonly ignoredInputTypes = new Set([
|
||||
@ -56,7 +57,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
"image",
|
||||
"file",
|
||||
]);
|
||||
private useTreeWalkerStrategyFlagSet = false;
|
||||
private useTreeWalkerStrategyFlagSet = true;
|
||||
|
||||
constructor(
|
||||
domElementVisibilityService: DomElementVisibilityService,
|
||||
@ -71,10 +72,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
}
|
||||
this.formFieldQueryString = `${inputQuery}, textarea:not([data-bwignore]), select:not([data-bwignore]), span[data-bwautofill]`;
|
||||
|
||||
void sendExtensionMessage("getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag").then(
|
||||
(useTreeWalkerStrategyFlag) =>
|
||||
(this.useTreeWalkerStrategyFlagSet = !!useTreeWalkerStrategyFlag?.result),
|
||||
);
|
||||
// void sendExtensionMessage("getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag").then(
|
||||
// (useTreeWalkerStrategyFlag) =>
|
||||
// (this.useTreeWalkerStrategyFlagSet = !!useTreeWalkerStrategyFlag?.result),
|
||||
// );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +120,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
}
|
||||
|
||||
this.domRecentlyMutated = false;
|
||||
return this.getFormattedPageDetails(autofillFormsData, autofillFieldsData);
|
||||
const pageDetails = this.getFormattedPageDetails(autofillFormsData, autofillFieldsData);
|
||||
this.setupInlineMenuListeners(pageDetails);
|
||||
|
||||
return pageDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,14 +281,11 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
*/
|
||||
private updateCachedAutofillFieldVisibility() {
|
||||
this.autofillFieldElements.forEach(async (autofillField, element) => {
|
||||
const currentViewableState = autofillField.viewable;
|
||||
const previouslyViewable = autofillField.viewable;
|
||||
autofillField.viewable = await this.domElementVisibilityService.isFormFieldViewable(element);
|
||||
|
||||
if (!currentViewableState && autofillField.viewable) {
|
||||
await this.autofillOverlayContentService?.setupInlineMenuListenerOnField(
|
||||
element,
|
||||
autofillField,
|
||||
);
|
||||
if (!previouslyViewable && autofillField.viewable) {
|
||||
this.setupInlineMenu(element, autofillField);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -458,10 +459,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
|
||||
if (elementIsSpanElement(element)) {
|
||||
this.cacheAutofillFieldElement(index, element, autofillFieldBase);
|
||||
void this.autofillOverlayContentService?.setupInlineMenuListenerOnField(
|
||||
element,
|
||||
autofillFieldBase,
|
||||
);
|
||||
return autofillFieldBase;
|
||||
}
|
||||
|
||||
@ -501,7 +498,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
};
|
||||
|
||||
this.cacheAutofillFieldElement(index, element, autofillField);
|
||||
void this.autofillOverlayContentService?.setupInlineMenuListenerOnField(element, autofillField);
|
||||
return autofillField;
|
||||
};
|
||||
|
||||
@ -533,11 +529,11 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
* @private
|
||||
*/
|
||||
private getAutoCompleteAttribute(element: ElementWithOpId<FormFieldElement>): string {
|
||||
const autoCompleteType =
|
||||
return (
|
||||
this.getPropertyOrAttribute(element, "x-autocompletetype") ||
|
||||
this.getPropertyOrAttribute(element, "autocompletetype") ||
|
||||
this.getPropertyOrAttribute(element, "autocomplete");
|
||||
return autoCompleteType !== "off" ? autoCompleteType : null;
|
||||
this.getPropertyOrAttribute(element, "autocomplete")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1196,13 +1192,13 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
* @private
|
||||
*/
|
||||
private updateAutofillElementsAfterMutation() {
|
||||
if (this.updateAutofillElementsAfterMutationTimeout) {
|
||||
clearTimeout(this.updateAutofillElementsAfterMutationTimeout);
|
||||
if (this.updateAfterMutationIdleCallback) {
|
||||
cancelIdleCallbackPolyfill(this.updateAfterMutationIdleCallback);
|
||||
}
|
||||
|
||||
this.updateAutofillElementsAfterMutationTimeout = setTimeout(
|
||||
this.updateAfterMutationIdleCallback = requestIdleCallbackPolyfill(
|
||||
this.getPageDetails.bind(this),
|
||||
this.updateAfterMutationTimeoutDelay,
|
||||
{ timeout: this.updateAfterMutationTimeout },
|
||||
);
|
||||
}
|
||||
|
||||
@ -1392,22 +1388,64 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
}
|
||||
|
||||
cachedAutofillFieldElement.viewable = true;
|
||||
void this.autofillOverlayContentService?.setupInlineMenuListenerOnField(
|
||||
formFieldElement,
|
||||
cachedAutofillFieldElement,
|
||||
);
|
||||
this.setupInlineMenu(formFieldElement, cachedAutofillFieldElement);
|
||||
|
||||
this.intersectionObserver?.unobserve(entry.target);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterates over all cached field elements and sets up the inline menu listeners on each field.
|
||||
*
|
||||
* @param pageDetails - The page details to use for the inline menu listeners
|
||||
*/
|
||||
private setupInlineMenuListeners(pageDetails: AutofillPageDetails) {
|
||||
if (!this.autofillOverlayContentService) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.autofillFieldElements.forEach((autofillField, formFieldElement) => {
|
||||
this.setupInlineMenu(formFieldElement, autofillField, pageDetails);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the inline menu listener on the passed field element.
|
||||
*
|
||||
* @param formFieldElement - The form field element to set up the inline menu listener on
|
||||
* @param autofillField - The metadata for the form field
|
||||
* @param pageDetails - The page details to use for the inline menu listeners
|
||||
*/
|
||||
private setupInlineMenu(
|
||||
formFieldElement: ElementWithOpId<FormFieldElement>,
|
||||
autofillField: AutofillField,
|
||||
pageDetails?: AutofillPageDetails,
|
||||
) {
|
||||
if (!this.autofillOverlayContentService) {
|
||||
return;
|
||||
}
|
||||
|
||||
const autofillPageDetails =
|
||||
pageDetails ||
|
||||
this.getFormattedPageDetails(
|
||||
this.getFormattedAutofillFormsData(),
|
||||
this.getFormattedAutofillFieldsData(),
|
||||
);
|
||||
|
||||
void this.autofillOverlayContentService.setupInlineMenu(
|
||||
formFieldElement,
|
||||
autofillField,
|
||||
autofillPageDetails,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the CollectAutofillContentService. Clears all
|
||||
* timeouts and disconnects the mutation observer.
|
||||
*/
|
||||
destroy() {
|
||||
if (this.updateAutofillElementsAfterMutationTimeout) {
|
||||
clearTimeout(this.updateAutofillElementsAfterMutationTimeout);
|
||||
if (this.updateAfterMutationIdleCallback) {
|
||||
cancelIdleCallbackPolyfill(this.updateAfterMutationIdleCallback);
|
||||
}
|
||||
this.mutationObserver?.disconnect();
|
||||
this.intersectionObserver?.disconnect();
|
||||
|
@ -0,0 +1,662 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import AutofillField from "../models/autofill-field";
|
||||
import AutofillForm from "../models/autofill-form";
|
||||
import AutofillPageDetails from "../models/autofill-page-details";
|
||||
|
||||
import { AutoFillConstants } from "./autofill-constants";
|
||||
import { InlineMenuFieldQualificationService } from "./inline-menu-field-qualification.service";
|
||||
|
||||
describe("InlineMenuFieldQualificationService", () => {
|
||||
let pageDetails: MockProxy<AutofillPageDetails>;
|
||||
let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
|
||||
|
||||
beforeEach(() => {
|
||||
pageDetails = mock<AutofillPageDetails>({
|
||||
forms: {},
|
||||
fields: [],
|
||||
});
|
||||
inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||
inlineMenuFieldQualificationService["inlineMenuFieldQualificationFlagSet"] = true;
|
||||
});
|
||||
|
||||
describe("isFieldForLoginForm", () => {
|
||||
describe("qualifying a password field for a login form", () => {
|
||||
describe("an invalid password field", () => {
|
||||
it("has a `new-password` autoCompleteType", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "new-password",
|
||||
});
|
||||
|
||||
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("has a type that is an excluded type", () => {
|
||||
AutoFillConstants.ExcludedAutofillLoginTypes.forEach((excludedType) => {
|
||||
const field = mock<AutofillField>({
|
||||
type: excludedType,
|
||||
});
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("has an attribute present on the FieldIgnoreList, indicating that the field is a captcha", () => {
|
||||
AutoFillConstants.FieldIgnoreList.forEach((attribute, index) => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: index === 0 ? attribute : "",
|
||||
htmlName: index === 1 ? attribute : "",
|
||||
placeholder: index > 1 ? attribute : "",
|
||||
});
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("has a type other than `password` or `text`", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "number",
|
||||
htmlID: "not-password",
|
||||
htmlName: "not-password",
|
||||
placeholder: "not-password",
|
||||
});
|
||||
|
||||
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("has a type of `text` without an attribute that indicates the field is a password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: "something-else",
|
||||
htmlName: "something-else",
|
||||
placeholder: "something-else",
|
||||
});
|
||||
|
||||
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("has a type of `text` and contains attributes that indicates the field is a search field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: "search",
|
||||
htmlName: "something-else",
|
||||
placeholder: "something-else",
|
||||
});
|
||||
|
||||
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
describe("does not have a parent form element", () => {
|
||||
beforeEach(() => {
|
||||
pageDetails.forms = {};
|
||||
});
|
||||
|
||||
it("on a page that has more than one password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "",
|
||||
});
|
||||
const secondField = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "some-other-password",
|
||||
htmlName: "some-other-password",
|
||||
placeholder: "some-other-password",
|
||||
});
|
||||
pageDetails.fields = [field, secondField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("on a page that has more than one visible username field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "",
|
||||
});
|
||||
const usernameField = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
const secondUsernameField = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: "some-other-user-username",
|
||||
htmlName: "some-other-user-username",
|
||||
placeholder: "some-other-user-username",
|
||||
});
|
||||
pageDetails.fields = [field, usernameField, secondUsernameField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("has a disabled `autocompleteType` value", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "",
|
||||
autoCompleteType: "off",
|
||||
});
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("has a parent form element", () => {
|
||||
let form: MockProxy<AutofillForm>;
|
||||
|
||||
beforeEach(() => {
|
||||
form = mock<AutofillForm>({ opid: "validFormId" });
|
||||
pageDetails.forms = {
|
||||
validFormId: form,
|
||||
};
|
||||
});
|
||||
|
||||
it("is structured with other password fields in the same form", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
});
|
||||
const secondField = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "some-other-password",
|
||||
htmlName: "some-other-password",
|
||||
placeholder: "some-other-password",
|
||||
form: "validFormId",
|
||||
});
|
||||
pageDetails.fields = [field, secondField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("a valid password field", () => {
|
||||
it("has an autoCompleteType of `current-password`", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
});
|
||||
|
||||
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("has a type of `text` with an attribute that indicates the field is a password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: null,
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
});
|
||||
|
||||
expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
describe("does not have a parent form element", () => {
|
||||
it("is the only password field on the page, has one username field on the page, and has a non-disabled `autocompleteType` value", () => {
|
||||
pageDetails.forms = {};
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "",
|
||||
autoCompleteType: "current-password",
|
||||
});
|
||||
const usernameField = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
pageDetails.fields = [field, usernameField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("has a parent form element", () => {
|
||||
let form: MockProxy<AutofillForm>;
|
||||
|
||||
beforeEach(() => {
|
||||
form = mock<AutofillForm>({ opid: "validFormId" });
|
||||
pageDetails.forms = {
|
||||
validFormId: form,
|
||||
};
|
||||
});
|
||||
|
||||
it("is the only password field within the form and has a visible username field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
});
|
||||
const secondPasswordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "some-other-password",
|
||||
htmlName: "some-other-password",
|
||||
placeholder: "some-other-password",
|
||||
form: "anotherFormId",
|
||||
});
|
||||
const usernameField = mock<AutofillField>({
|
||||
type: "text",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
pageDetails.fields = [field, secondPasswordField, usernameField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is the only password field within the form and has a non-disabled `autocompleteType` value", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
autoCompleteType: "",
|
||||
});
|
||||
const secondPasswordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
htmlID: "some-other-password",
|
||||
htmlName: "some-other-password",
|
||||
placeholder: "some-other-password",
|
||||
form: "anotherFormId",
|
||||
});
|
||||
pageDetails.fields = [field, secondPasswordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("qualifying a username field for a login form", () => {
|
||||
describe("an invalid username field", () => {
|
||||
["username", "email"].forEach((autoCompleteType) => {
|
||||
it(`has a ${autoCompleteType} 'autoCompleteType' value when structured on a page with new password fields`, () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType,
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "new-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
["new", "change", "neue", "ändern"].forEach((keyword) => {
|
||||
it(`has a keyword of ${keyword} that indicates a 'new or changed' username is being filled`, () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: `${keyword} username`,
|
||||
});
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("does not have a parent form element", () => {
|
||||
beforeEach(() => {
|
||||
pageDetails.forms = {};
|
||||
});
|
||||
|
||||
it("is structured on a page with multiple password fields", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
});
|
||||
const secondPasswordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "some-other-password",
|
||||
htmlName: "some-other-password",
|
||||
placeholder: "some-other-password",
|
||||
});
|
||||
pageDetails.fields = [field, passwordField, secondPasswordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("has a parent form element", () => {
|
||||
let form: MockProxy<AutofillForm>;
|
||||
|
||||
beforeEach(() => {
|
||||
form = mock<AutofillForm>({ opid: "validFormId" });
|
||||
pageDetails.forms = {
|
||||
validFormId: form,
|
||||
};
|
||||
});
|
||||
|
||||
it("is structured on a page with no password fields and has a disabled `autoCompleteType` value", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "off",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
pageDetails.fields = [field];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is structured on a page with no password fields but has other types of fields in the form", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
const otherField = mock<AutofillField>({
|
||||
type: "number",
|
||||
autoCompleteType: "",
|
||||
htmlID: "some-other-field",
|
||||
htmlName: "some-other-field",
|
||||
placeholder: "some-other-field",
|
||||
form: "validFormId",
|
||||
});
|
||||
pageDetails.fields = [field, otherField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is structured on a page with multiple viewable password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
});
|
||||
const secondPasswordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "some-other-password",
|
||||
htmlName: "some-other-password",
|
||||
placeholder: "some-other-password",
|
||||
form: "validFormId",
|
||||
});
|
||||
pageDetails.fields = [field, passwordField, secondPasswordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("is structured on a page with a with no visible password fields and but contains a disabled autocomplete type", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "off",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
viewable: false,
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("a valid username field", () => {
|
||||
["username", "email"].forEach((autoCompleteType) => {
|
||||
it(`has a ${autoCompleteType} 'autoCompleteType' value`, () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType,
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("does not have a parent form element", () => {
|
||||
beforeEach(() => {
|
||||
pageDetails.forms = {};
|
||||
});
|
||||
|
||||
it("is structured on a page with a single visible password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "off",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is structured on a page with a single non-visible password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "off",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
viewable: false,
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("has a non-disabled autoCompleteType and is structured on a page with no other password fields", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
});
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("has a parent form element", () => {
|
||||
let form: MockProxy<AutofillForm>;
|
||||
|
||||
beforeEach(() => {
|
||||
form = mock<AutofillForm>({ opid: "validFormId" });
|
||||
pageDetails.forms = {
|
||||
validFormId: form,
|
||||
};
|
||||
});
|
||||
|
||||
it("is structured on a page with a single password field", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is structured on a page with a with no visible password fields and a non-disabled autocomplete type", () => {
|
||||
const field = mock<AutofillField>({
|
||||
type: "text",
|
||||
autoCompleteType: "",
|
||||
htmlID: "user-username",
|
||||
htmlName: "user-username",
|
||||
placeholder: "user-username",
|
||||
form: "validFormId",
|
||||
});
|
||||
const passwordField = mock<AutofillField>({
|
||||
type: "password",
|
||||
autoCompleteType: "current-password",
|
||||
htmlID: "user-password",
|
||||
htmlName: "user-password",
|
||||
placeholder: "user-password",
|
||||
form: "validFormId",
|
||||
viewable: false,
|
||||
});
|
||||
pageDetails.fields = [field, passwordField];
|
||||
|
||||
expect(
|
||||
inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,438 @@
|
||||
import AutofillField from "../models/autofill-field";
|
||||
import AutofillPageDetails from "../models/autofill-page-details";
|
||||
import { sendExtensionMessage } from "../utils";
|
||||
|
||||
import { InlineMenuFieldQualificationService as InlineMenuFieldQualificationsServiceInterface } from "./abstractions/inline-menu-field-qualifications.service";
|
||||
import { AutoFillConstants } from "./autofill-constants";
|
||||
|
||||
export class InlineMenuFieldQualificationService
|
||||
implements InlineMenuFieldQualificationsServiceInterface
|
||||
{
|
||||
private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
|
||||
private excludedAutofillLoginTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes);
|
||||
private usernameFieldTypes = new Set(["text", "email", "number", "tel"]);
|
||||
private usernameAutocompleteValues = new Set(["username", "email"]);
|
||||
private fieldIgnoreListString = AutoFillConstants.FieldIgnoreList.join(",");
|
||||
private passwordFieldExcludeListString = AutoFillConstants.PasswordFieldExcludeList.join(",");
|
||||
private autofillFieldKeywordsMap: WeakMap<AutofillField, string> = new WeakMap();
|
||||
private autocompleteDisabledValues = new Set(["off", "false"]);
|
||||
private newFieldKeywords = new Set(["new", "change", "neue", "ändern"]);
|
||||
private inlineMenuFieldQualificationFlagSet = false;
|
||||
|
||||
constructor() {
|
||||
void sendExtensionMessage("getInlineMenuFieldQualificationFeatureFlag").then(
|
||||
(getInlineMenuFieldQualificationFlag) =>
|
||||
(this.inlineMenuFieldQualificationFlagSet = !!getInlineMenuFieldQualificationFlag?.result),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field as a field for a login form.
|
||||
*
|
||||
* @param field - The field to validate, should be a username or password field.
|
||||
* @param pageDetails - The details of the page that the field is on.
|
||||
*/
|
||||
isFieldForLoginForm(field: AutofillField, pageDetails: AutofillPageDetails): boolean {
|
||||
if (!this.inlineMenuFieldQualificationFlagSet) {
|
||||
return this.isFieldForLoginFormFallback(field);
|
||||
}
|
||||
|
||||
const isCurrentPasswordField = this.isCurrentPasswordField(field);
|
||||
if (isCurrentPasswordField) {
|
||||
return this.isPasswordFieldForLoginForm(field, pageDetails);
|
||||
}
|
||||
|
||||
const isUsernameField = this.isUsernameField(field);
|
||||
if (!isUsernameField) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.isUsernameFieldForLoginForm(field, pageDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field as a password field for a login form.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
* @param pageDetails - The details of the page that the field is on.
|
||||
*/
|
||||
private isPasswordFieldForLoginForm(
|
||||
field: AutofillField,
|
||||
pageDetails: AutofillPageDetails,
|
||||
): boolean {
|
||||
// If the provided field is set with an autocomplete value of "current-password", we should assume that
|
||||
// the page developer intends for this field to be interpreted as a password field for a login form.
|
||||
if (field.autoCompleteType === "current-password") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const usernameFieldsInPageDetails = pageDetails.fields.filter(this.isUsernameField);
|
||||
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
|
||||
|
||||
// If a single username and a single password field exists on the page, we
|
||||
// should assume that this field is part of a login form.
|
||||
if (usernameFieldsInPageDetails.length === 1 && passwordFieldsInPageDetails.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the field is not structured within a form, we need to identify if the field is present on
|
||||
// a page with multiple password fields. If that isn't the case, we can assume this is a login form field.
|
||||
const parentForm = pageDetails.forms[field.form];
|
||||
if (!parentForm) {
|
||||
// If no parent form is found, and multiple password fields are present, we should assume that
|
||||
// the passed field belongs to a user account creation form.
|
||||
if (passwordFieldsInPageDetails.length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If multiple username fields exist on the page, we should assume that
|
||||
// the provided field is part of an account creation form.
|
||||
const visibleUsernameFields = usernameFieldsInPageDetails.filter((f) => f.viewable);
|
||||
if (visibleUsernameFields.length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a single username field or less is present on the page, then we can assume that the
|
||||
// provided field is for a login form. This will only be the case if the field does not
|
||||
// explicitly have its autocomplete attribute set to "off" or "false".
|
||||
return !this.autocompleteDisabledValues.has(field.autoCompleteType);
|
||||
}
|
||||
|
||||
// If the field has a form parent and there are multiple visible password fields
|
||||
// in the form, this is not a login form field
|
||||
const visiblePasswordFieldsInPageDetails = passwordFieldsInPageDetails.filter(
|
||||
(f) => f.form === field.form && f.viewable,
|
||||
);
|
||||
if (visiblePasswordFieldsInPageDetails.length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the form has any visible username fields, we should treat the field as part of a login form
|
||||
const visibleUsernameFields = usernameFieldsInPageDetails.filter(
|
||||
(f) => f.form === field.form && f.viewable,
|
||||
);
|
||||
if (visibleUsernameFields.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the field has a form parent and no username field exists and the field has an
|
||||
// autocomplete attribute set to "off" or "false", this is not a password field
|
||||
return !this.autocompleteDisabledValues.has(field.autoCompleteType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field as a username field for a login form.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
* @param pageDetails - The details of the page that the field is on.
|
||||
*/
|
||||
private isUsernameFieldForLoginForm(
|
||||
field: AutofillField,
|
||||
pageDetails: AutofillPageDetails,
|
||||
): boolean {
|
||||
// If the provided field is set with an autocomplete of "username", we should assume that
|
||||
// the page developer intends for this field to be interpreted as a username field.
|
||||
if (this.usernameAutocompleteValues.has(field.autoCompleteType)) {
|
||||
const newPasswordFieldsInPageDetails = pageDetails.fields.filter(this.isNewPasswordField);
|
||||
return newPasswordFieldsInPageDetails.length === 0;
|
||||
}
|
||||
|
||||
// If any keywords in the field's data indicates that this is a field for a "new" or "changed"
|
||||
// username, we should assume that this field is not for a login form.
|
||||
if (this.keywordsFoundInFieldData(field, [...this.newFieldKeywords])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the field is not explicitly set as a username field, we need to qualify
|
||||
// the field based on the other fields that are present on the page.
|
||||
const parentForm = pageDetails.forms[field.form];
|
||||
const passwordFieldsInPageDetails = pageDetails.fields.filter(this.isCurrentPasswordField);
|
||||
|
||||
// If the field is not structured within a form, we need to identify if the field is used in conjunction
|
||||
// with a password field. If that's the case, then we should assume that it is a form field element.
|
||||
if (!parentForm) {
|
||||
// If a formless field is present in a webpage with a single password field, we
|
||||
// should assume that it is part of a login workflow.
|
||||
const visiblePasswordFieldsInPageDetails = passwordFieldsInPageDetails.filter(
|
||||
(passwordField) => passwordField.viewable,
|
||||
);
|
||||
if (visiblePasswordFieldsInPageDetails.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If more than a single password field exists on the page, we should assume that the field
|
||||
// is part of an account creation form.
|
||||
if (visiblePasswordFieldsInPageDetails.length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no visible fields are found on the page, but we have a single password
|
||||
// field we should assume that the field is part of a login form.
|
||||
if (passwordFieldsInPageDetails.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the page does not contain any password fields, it might be part of a multistep login form.
|
||||
// That will only be the case if the field does not explicitly have its autocomplete attribute
|
||||
// set to "off" or "false".
|
||||
return !this.autocompleteDisabledValues.has(field.autoCompleteType);
|
||||
}
|
||||
|
||||
// If the field is structured within a form, but no password fields are present in the form,
|
||||
// we need to consider whether the field is part of a multistep login form.
|
||||
if (passwordFieldsInPageDetails.length === 0) {
|
||||
// If the field's autocomplete is set to a disabled value, we should assume that the field is
|
||||
// not part of a login form.
|
||||
if (this.autocompleteDisabledValues.has(field.autoCompleteType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the form that contains the field has more than one visible field, we should assume
|
||||
// that the field is part of an account creation form.
|
||||
const fieldsWithinForm = pageDetails.fields.filter(
|
||||
(pageDetailsField) => pageDetailsField.form === field.form && pageDetailsField.viewable,
|
||||
);
|
||||
return fieldsWithinForm.length === 1;
|
||||
}
|
||||
|
||||
// If a single password field exists within the page details, and that password field is part of
|
||||
// the same form as the provided field, we should assume that the field is part of a login form.
|
||||
const visiblePasswordFieldsInPageDetails = passwordFieldsInPageDetails.filter(
|
||||
(passwordField) => passwordField.form === field.form && passwordField.viewable,
|
||||
);
|
||||
if (visiblePasswordFieldsInPageDetails.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If multiple visible password fields exist within the page details, we need to assume that the
|
||||
// provided field is part of an account creation form.
|
||||
if (visiblePasswordFieldsInPageDetails.length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no visible password fields are found, this field might be part of a multipart form.
|
||||
// Check for an invalid autocompleteType to determine if the field is part of a login form.
|
||||
return !this.autocompleteDisabledValues.has(field.autoCompleteType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field as a username field.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private isUsernameField = (field: AutofillField): boolean => {
|
||||
if (
|
||||
!this.usernameFieldTypes.has(field.type) ||
|
||||
this.isExcludedFieldType(field, this.excludedAutofillLoginTypesSet)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.keywordsFoundInFieldData(field, AutoFillConstants.UsernameFieldNames);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the provided field as a current password field.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private isCurrentPasswordField = (field: AutofillField): boolean => {
|
||||
if (field.autoCompleteType === "new-password") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.isPasswordField(field);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the provided field as a new password field.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private isNewPasswordField = (field: AutofillField): boolean => {
|
||||
if (field.autoCompleteType === "current-password") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.isPasswordField(field);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the provided field as a password field.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private isPasswordField = (field: AutofillField): boolean => {
|
||||
const isInputPasswordType = field.type === "password";
|
||||
if (
|
||||
(!isInputPasswordType &&
|
||||
this.isExcludedFieldType(field, this.excludedAutofillLoginTypesSet)) ||
|
||||
this.fieldHasDisqualifyingAttributeValue(field)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isInputPasswordType || this.isLikePasswordField(field);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the provided field as a field to indicate if the
|
||||
* field potentially acts as a password field.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private isLikePasswordField(field: AutofillField): boolean {
|
||||
if (field.type !== "text") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const testedValues = [field.htmlID, field.htmlName, field.placeholder];
|
||||
for (let i = 0; i < testedValues.length; i++) {
|
||||
if (this.valueIsLikePassword(testedValues[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided value to indicate if the value is like a password.
|
||||
*
|
||||
* @param value - The value to validate
|
||||
*/
|
||||
private valueIsLikePassword(value: string): boolean {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
// Removes all whitespace, _ and - characters
|
||||
const cleanedValue = value.toLowerCase().replace(/[\s_-]/g, "");
|
||||
|
||||
if (cleanedValue.indexOf("password") < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(this.passwordFieldExcludeListString.indexOf(cleanedValue) > -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field to indicate if the field has a
|
||||
* disqualifying attribute that would impede autofill entirely.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private fieldHasDisqualifyingAttributeValue(field: AutofillField): boolean {
|
||||
const checkedAttributeValues = [field.htmlID, field.htmlName, field.placeholder];
|
||||
|
||||
for (let i = 0; i < checkedAttributeValues.length; i++) {
|
||||
const checkedAttributeValue = checkedAttributeValues[i];
|
||||
const cleanedValue = checkedAttributeValue?.toLowerCase().replace(/[\s_-]/g, "");
|
||||
|
||||
if (cleanedValue && this.fieldIgnoreListString.indexOf(cleanedValue) > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field to indicate if the field is excluded from autofill.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
* @param excludedTypes - The set of excluded types
|
||||
*/
|
||||
private isExcludedFieldType(field: AutofillField, excludedTypes: Set<string>): boolean {
|
||||
if (excludedTypes.has(field.type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isSearchField(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field to indicate if the field is a search field.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
*/
|
||||
private isSearchField(field: AutofillField): boolean {
|
||||
const matchFieldAttributeValues = [field.type, field.htmlName, field.htmlID, field.placeholder];
|
||||
for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
|
||||
if (!matchFieldAttributeValues[attrIndex]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Separate camel case words and case them to lower case values
|
||||
const camelCaseSeparatedFieldAttribute = matchFieldAttributeValues[attrIndex]
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.toLowerCase();
|
||||
// Split the attribute by non-alphabetical characters to get the keywords
|
||||
const attributeKeywords = camelCaseSeparatedFieldAttribute.split(/[^a-z]/gi);
|
||||
|
||||
for (let keywordIndex = 0; keywordIndex < attributeKeywords.length; keywordIndex++) {
|
||||
if (this.searchFieldNamesSet.has(attributeKeywords[keywordIndex])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the provided field to indicate if the field has any of the provided keywords.
|
||||
*
|
||||
* @param autofillFieldData - The field data to search for keywords
|
||||
* @param keywords - The keywords to search for
|
||||
*/
|
||||
private keywordsFoundInFieldData(autofillFieldData: AutofillField, keywords: string[]) {
|
||||
const searchedString = this.getAutofillFieldDataKeywords(autofillFieldData);
|
||||
return keywords.some((keyword) => searchedString.includes(keyword));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the keywords from the provided autofill field data.
|
||||
*
|
||||
* @param autofillFieldData - The field data to search for keywords
|
||||
*/
|
||||
private getAutofillFieldDataKeywords(autofillFieldData: AutofillField) {
|
||||
if (this.autofillFieldKeywordsMap.has(autofillFieldData)) {
|
||||
return this.autofillFieldKeywordsMap.get(autofillFieldData);
|
||||
}
|
||||
|
||||
const keywordValues = [
|
||||
autofillFieldData.htmlID,
|
||||
autofillFieldData.htmlName,
|
||||
autofillFieldData.htmlClass,
|
||||
autofillFieldData.type,
|
||||
autofillFieldData.title,
|
||||
autofillFieldData.placeholder,
|
||||
autofillFieldData.autoCompleteType,
|
||||
autofillFieldData["label-data"],
|
||||
autofillFieldData["label-aria"],
|
||||
autofillFieldData["label-left"],
|
||||
autofillFieldData["label-right"],
|
||||
autofillFieldData["label-tag"],
|
||||
autofillFieldData["label-top"],
|
||||
]
|
||||
.join(",")
|
||||
.toLowerCase();
|
||||
this.autofillFieldKeywordsMap.set(autofillFieldData, keywordValues);
|
||||
|
||||
return keywordValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method represents the previous rudimentary approach to qualifying fields for login forms.
|
||||
*
|
||||
* @param field - The field to validate
|
||||
* @deprecated - This method will only be used when the fallback flag is set to true.
|
||||
*/
|
||||
private isFieldForLoginFormFallback(field: AutofillField): boolean {
|
||||
if (field.type === "password") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isUsernameField(field);
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { EVENTS } from "@bitwarden/common/autofill/constants";
|
||||
|
||||
import AutofillScript, { FillScript, FillScriptActions } from "../models/autofill-script";
|
||||
import { mockQuerySelectorAllDefinedCall } from "../spec/testing-utils";
|
||||
import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types";
|
||||
|
||||
import { InlineMenuFieldQualificationService } from "./abstractions/inline-menu-field-qualifications.service";
|
||||
import { AutofillOverlayContentService } from "./autofill-overlay-content.service";
|
||||
import CollectAutofillContentService from "./collect-autofill-content.service";
|
||||
import DomElementVisibilityService from "./dom-element-visibility.service";
|
||||
@ -64,8 +67,11 @@ function setMockWindowLocation({
|
||||
}
|
||||
|
||||
describe("InsertAutofillContentService", () => {
|
||||
const inlineMenuFieldQualificationService = mock<InlineMenuFieldQualificationService>();
|
||||
const domElementVisibilityService = new DomElementVisibilityService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService(
|
||||
inlineMenuFieldQualificationService,
|
||||
);
|
||||
const collectAutofillContentService = new CollectAutofillContentService(
|
||||
domElementVisibilityService,
|
||||
autofillOverlayContentService,
|
||||
|
@ -21,7 +21,10 @@ export function generateRandomChars(length: number): string {
|
||||
* @param callback - The callback function to run when the browser is idle.
|
||||
* @param options - The options to pass to the requestIdleCallback function.
|
||||
*/
|
||||
export function requestIdleCallbackPolyfill(callback: () => void, options?: Record<string, any>) {
|
||||
export function requestIdleCallbackPolyfill(
|
||||
callback: () => void,
|
||||
options?: Record<string, any>,
|
||||
): number | NodeJS.Timeout {
|
||||
if ("requestIdleCallback" in globalThis) {
|
||||
return globalThis.requestIdleCallback(() => callback(), options);
|
||||
}
|
||||
@ -29,6 +32,19 @@ export function requestIdleCallbackPolyfill(callback: () => void, options?: Reco
|
||||
return globalThis.setTimeout(() => callback(), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polyfills the cancelIdleCallback API with a clearTimeout fallback.
|
||||
*
|
||||
* @param id - The ID of the idle callback to cancel.
|
||||
*/
|
||||
export function cancelIdleCallbackPolyfill(id: NodeJS.Timeout | number) {
|
||||
if ("cancelIdleCallback" in globalThis) {
|
||||
return globalThis.cancelIdleCallback(id as number);
|
||||
}
|
||||
|
||||
return globalThis.clearTimeout(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string of characters that formatted as a custom element name.
|
||||
*/
|
||||
|
@ -745,7 +745,6 @@ export default class MainBackground {
|
||||
this.folderApiService = new FolderApiService(this.folderService, this.apiService);
|
||||
|
||||
this.userVerificationService = new UserVerificationService(
|
||||
this.stateService,
|
||||
this.cryptoService,
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
|
@ -69,6 +69,7 @@ export default class RuntimeBackground {
|
||||
const messagesWithResponse = [
|
||||
"biometricUnlock",
|
||||
"getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag",
|
||||
"getInlineMenuFieldQualificationFeatureFlag",
|
||||
];
|
||||
|
||||
if (messagesWithResponse.includes(msg.command)) {
|
||||
@ -186,6 +187,9 @@ export default class RuntimeBackground {
|
||||
FeatureFlag.UseTreeWalkerApiForPageDetailsCollection,
|
||||
);
|
||||
}
|
||||
case "getInlineMenuFieldQualificationFeatureFlag": {
|
||||
return await this.configService.getFeatureFlag(FeatureFlag.InlineMenuFieldQualification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.6.0",
|
||||
"version": "2024.6.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
@ -3,7 +3,7 @@
|
||||
"minimum_chrome_version": "102.0",
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.6.0",
|
||||
"version": "2024.6.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
@ -8,6 +8,16 @@ import {
|
||||
tdeDecryptionRequiredGuard,
|
||||
unauthGuardFn,
|
||||
} from "@bitwarden/angular/auth/guards";
|
||||
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
|
||||
import {
|
||||
AnonLayoutWrapperComponent,
|
||||
AnonLayoutWrapperData,
|
||||
RegistrationFinishComponent,
|
||||
RegistrationStartComponent,
|
||||
RegistrationStartSecondaryComponent,
|
||||
RegistrationStartSecondaryComponentData,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
|
||||
import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard";
|
||||
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
||||
@ -343,6 +353,45 @@ const routes: Routes = [
|
||||
canActivate: [AuthGuard],
|
||||
data: { state: "update-temp-password" },
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
component: AnonLayoutWrapperComponent,
|
||||
children: [
|
||||
{
|
||||
path: "signup",
|
||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||
data: { pageTitle: "createAccount" } satisfies AnonLayoutWrapperData,
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: RegistrationStartComponent,
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
component: RegistrationStartSecondaryComponent,
|
||||
outlet: "secondary",
|
||||
data: {
|
||||
loginRoute: "/home",
|
||||
} satisfies RegistrationStartSecondaryComponentData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "finish-signup",
|
||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||
data: {
|
||||
pageTitle: "setAStrongPassword",
|
||||
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
|
||||
} satisfies AnonLayoutWrapperData,
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: RegistrationFinishComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
...extensionRefreshSwap(AboutPageComponent, AboutPageV2Component, {
|
||||
path: "about",
|
||||
canActivate: [AuthGuard],
|
||||
|
@ -57,12 +57,12 @@
|
||||
>
|
||||
<div class="vault-select-org-text-container">
|
||||
<i
|
||||
*ngIf="organization.planProductType !== 1"
|
||||
*ngIf="organization.productTierType !== 1"
|
||||
class="bwi bwi-fw bwi-business vault-select-prefix-icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="organization.planProductType === 1"
|
||||
*ngIf="organization.productTierType === 1"
|
||||
class="bwi bwi-fw bwi-family vault-select-prefix-icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
@ -5,7 +5,7 @@ import { BehaviorSubject } from "rxjs";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { ObservableTracker } from "@bitwarden/common/spec";
|
||||
import { CipherId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@ -78,7 +78,7 @@ describe("VaultPopupItemsService", () => {
|
||||
mockOrg = {
|
||||
id: "org1",
|
||||
name: "Organization 1",
|
||||
planProductType: ProductType.Enterprise,
|
||||
productTierType: ProductTierType.Enterprise,
|
||||
} as Organization;
|
||||
|
||||
mockCollections = [
|
||||
|
@ -6,7 +6,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
@ -206,7 +206,7 @@ describe("VaultPopupListFiltersService", () => {
|
||||
name: "family org",
|
||||
id: "1234-3323-23223",
|
||||
enabled: true,
|
||||
planProductType: ProductType.Families,
|
||||
productTierType: ProductTierType.Families,
|
||||
},
|
||||
] as Organization[];
|
||||
|
||||
@ -224,7 +224,7 @@ describe("VaultPopupListFiltersService", () => {
|
||||
name: "free org",
|
||||
id: "1234-3323-23223",
|
||||
enabled: true,
|
||||
planProductType: ProductType.Free,
|
||||
productTierType: ProductTierType.Free,
|
||||
},
|
||||
] as Organization[];
|
||||
|
||||
@ -242,7 +242,7 @@ describe("VaultPopupListFiltersService", () => {
|
||||
name: "free org",
|
||||
id: "1234-3323-23223",
|
||||
enabled: false,
|
||||
planProductType: ProductType.Free,
|
||||
productTierType: ProductTierType.Free,
|
||||
},
|
||||
] as Organization[];
|
||||
|
||||
|
@ -16,7 +16,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@ -216,8 +216,8 @@ export class VaultPopupListFiltersService {
|
||||
// Show a warning icon if the organization is deactivated
|
||||
icon = "bwi-exclamation-triangle tw-text-danger";
|
||||
} else if (
|
||||
org.planProductType === ProductType.Families ||
|
||||
org.planProductType === ProductType.Free
|
||||
org.productTierType === ProductTierType.Families ||
|
||||
org.productTierType === ProductTierType.Free
|
||||
) {
|
||||
// Show a family icon if the organization is a family or free org
|
||||
icon = "bwi-family";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||
|
||||
@ -26,13 +26,13 @@ export class PopupCipherView extends CipherView {
|
||||
* Get the bwi icon for the cipher according to the organization type.
|
||||
*/
|
||||
get orgIcon(): "bwi-family" | "bwi-business" | null {
|
||||
switch (this.organization?.planProductType) {
|
||||
case ProductType.Free:
|
||||
case ProductType.Families:
|
||||
switch (this.organization?.productTierType) {
|
||||
case ProductTierType.Free:
|
||||
case ProductTierType.Families:
|
||||
return "bwi-family";
|
||||
case ProductType.Teams:
|
||||
case ProductType.Enterprise:
|
||||
case ProductType.TeamsStarter:
|
||||
case ProductTierType.Teams:
|
||||
case ProductTierType.Enterprise:
|
||||
case ProductTierType.TeamsStarter:
|
||||
return "bwi-business";
|
||||
default:
|
||||
return null;
|
||||
|
@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Name" xml:space="preserve">
|
||||
<value>Bitwarden Password Manager</value>
|
||||
<value>Bitwarden 비밀번호 관리자</value>
|
||||
</data>
|
||||
<data name="Summary" xml:space="preserve">
|
||||
<value>집에서도, 직장에서도, 이동 중에도 Bitwarden은 비밀번호, 패스키, 민감 정보를 쉽게 보호합니다.</value>
|
||||
|
@ -124,48 +124,48 @@
|
||||
<value>Вдома, на роботі чи в дорозі, Bitwarden захищає ваші паролі, ключі доступу та конфіденційну інформацію.</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>Визнаний найкращим менеджером паролів за версією PCMag, WIRED, The Verge, CNET, G2 та інших!
|
||||
<value>Визнаний найкращим менеджером паролів виданнями PCMag, WIRED, The Verge, CNET, G2 та іншими!
|
||||
|
||||
ЗАХИСТІТЬ СВОЄ ЦИФРОВЕ ЖИТТЯ
|
||||
Убезпечте своє цифрове життя та захистіться від витоку даних, створивши та зберігши унікальні, надійні паролі для кожного облікового запису. Зберігайте всі дані в наскрізному зашифрованому сховищі паролів, доступ до якого маєте лише ви.
|
||||
Убезпечте своє цифрове життя та захистіться від витоків даних, генеруючи та зберігаючи унікальні надійні паролі для кожного облікового запису. Зберігайте все в наскрізно зашифрованому сховищі паролів, доступ до якого маєте тільки ви.
|
||||
|
||||
ОТРИМУВАТИ ДОСТУП ДО СВОЇХ ДАНИХ БУДЬ-ДЕ, БУДЬ-КОЛИ, БУДЬ НА ЯКОМУ ПРИСТРОЇ
|
||||
Легко керуйте, зберігайте, захищайте та діліться необмеженою кількістю паролів на необмеженій кількості пристроїв без обмежень.
|
||||
ДОСТУП ДО ДАНИХ БУДЬ-ДЕ, БУДЬ-КОЛИ, НА БУДЬ-ЯКОМУ ПРИСТРОЇ
|
||||
Легко керуйте, зберігайте, захищайте та діліться необмеженою кількістю паролів на необмеженій кількості пристроїв.
|
||||
|
||||
КОЖЕН ПОВИНЕН МАТИ ІНСТРУМЕНТИ, ЩОБ ЗАЛИШАТИСЯ В БЕЗПЕЦІ В ІНТЕРНЕТІ
|
||||
Користуйтеся Bitwarden безкоштовно, без реклами і без продажу даних. Bitwarden вважає, що кожен повинен мати можливість залишатися в безпеці в Інтернеті. Преміум-плани пропонують доступ до розширених функцій.
|
||||
КОЖЕН ПОВИНЕН МАТИ ІНСТРУМЕНТИ ДЛЯ БЕЗПЕКИ В ІНТЕРНЕТІ
|
||||
Використовуйте Bitwarden безплатно без реклами або продажу даних. Bitwarden вважає, що кожен повинен мати можливість залишатися в безпеці в Інтернеті. Завдяки тарифним планам Преміум можна отримати доступ до розширених можливостей.
|
||||
|
||||
РОЗШИРЮЙТЕ МОЖЛИВОСТІ СВОЇХ КОМАНД ЗА ДОПОМОГОЮ BITWARDEN
|
||||
Плани для Команд та Підприємства містять професійні бізнес-функції. Деякі приклади включають інтеграцію SSO, самостійний хостинг, інтеграцію каталогів і забезпечення SCIM, глобальні політики, доступ до API, журнали подій і багато іншого.
|
||||
РОЗШИРТЕ МОЖЛИВОСТІ СВОЇХ КОМАНД ЗА ДОПОМОГОЮ BITWARDEN
|
||||
Плани для команд і компаній мають професійні бізнес-функції. Вони передбачають інтеграцію єдиного входу (SSO), власне розміщення, інтеграцію каталогів та забезпечення SCIM, глобальні політики, доступ до API, журнали подій тощо.
|
||||
|
||||
Використовуйте Bitwarden, щоб захистити своїх співробітників і ділитися конфіденційною інформацією з колегами.
|
||||
Використовуйте Bitwarden для захисту персоналу та обміну конфіденційною інформацією з колегами.
|
||||
|
||||
|
||||
Більше причин вибрати Bitwarden:
|
||||
Інші причини для вибору Bitwarden:
|
||||
|
||||
Шифрування світового класу
|
||||
Паролі захищені вдосконаленим наскрізним шифруванням (біт AES-256, солоний хештег і PBKDF2 SHA-256), тому ваші дані залишаються надійно захищеними та конфіденційними.
|
||||
Паролі захищаються розширеним наскрізним шифруванням (AES-256, сіллю хешування і PBKDF2 SHA-256), тому ваші дані завжди зберігаються приватно і в безпеці.
|
||||
|
||||
Аудит третьої сторони
|
||||
Bitwarden регулярно проводить комплексні сторонні аудити безпеки з відомими компаніями, що займаються безпекою. Ці щорічні аудити включають оцінку вихідного коду та тестування на проникнення на всіх IP-адресах, серверах і веб-додатках Bitwarden.
|
||||
Сторонні аудити
|
||||
Bitwarden регулярно проводить комплексні аудити безпеки із залученням третіх сторін – відомих компаній у сфері безпеки. Під час цих щорічних аудитів проводиться оцінка програмного коду і тестування на проникнення через IP-адреси Bitwarden, сервери та вебпрограми.
|
||||
|
||||
Розширений 2FA
|
||||
Захистіть свій вхід за допомогою стороннього автентифікатора, кодів, надісланих електронною поштою, або облікових даних FIDO2 WebAuthn, наприклад, апаратного ключа безпеки або пароля.
|
||||
Розширена 2FA
|
||||
Захистіть свої дані входу за допомогою стороннього автентифікатора, кодів, що надсилаються електронною поштою, або облікових даних FIDO2 WebAuthn, як-от апаратний ключ безпеки або ключ доступу.
|
||||
|
||||
Bitwarden Send
|
||||
Передавайте дані безпосередньо іншим, зберігаючи при цьому наскрізну зашифровану безпеку та обмежуючи вразливість.
|
||||
Відправлення Bitwarden
|
||||
Передавайте дані безпосередньо іншим користувачам, зберігаючи наскрізне шифрування та обмежуючи їх викриття.
|
||||
|
||||
Вбудований генератор
|
||||
Створюйте довгі, складні та відмінні паролі та унікальні імена користувачів для кожного сайту, який ви відвідуєте. Інтеграція з провайдерами псевдонімів електронної пошти для додаткової конфіденційності.
|
||||
Створюйте довгі, складні та чіткі паролі, а також унікальні імена користувачів для кожного сайту, який ви відвідуєте. Користуйтеся інтеграцією з провайдерами псевдонімів електронної пошти для забезпечення додаткової приватності.
|
||||
|
||||
Глобальні переклади
|
||||
Переклади Bitwarden існують для більш ніж 60 мов, перекладені світовою спільнотою за допомогою Crowdin.
|
||||
Переклад різними мовами
|
||||
Bitwarden перекладено понад 60 мовами завдяки зусиллям нашої світової спільноти на Crowdin.
|
||||
|
||||
Крос-платформні додатки
|
||||
Захищайте конфіденційні дані у своєму сховищі Bitwarden Vault та діліться ними з будь-якого браузера, мобільного пристрою, настільної операційної системи тощо.
|
||||
Програми для різних платформ
|
||||
Захищайте та діліться конфіденційними даними в межах свого сховища Bitwarden з будь-якого браузера, мобільного пристрою або комп'ютерної ОС, а також інших можливостей.
|
||||
|
||||
Bitwarden захищає більше, ніж просто паролі
|
||||
Наскрізні рішення для управління зашифрованими обліковими даними від Bitwarden дозволяють організаціям захистити все, включаючи секрети розробників і досвід роботи з ключами. Відвідайте Bitwarden.com, щоб дізнатися більше про Bitwarden Secrets Manager і Bitwarden Passwordless.dev!
|
||||
Bitwarden захищає не лише паролі
|
||||
Комплексні рішення для керування наскрізно зашифрованими обліковими даними від Bitwarden дають змогу організаціям захищати все, включно з секретами розробників та ключами доступу. Відвідайте Bitwarden.com, щоб дізнатися більше про Менеджер секретів Bitwarden і Bitwarden Passwordless.dev!
|
||||
</value>
|
||||
</data>
|
||||
<data name="AssetTitle" xml:space="preserve">
|
||||
|
@ -4,6 +4,7 @@ const config = require("../../libs/components/tailwind.config.base");
|
||||
config.content = [
|
||||
"./src/**/*.{html,ts}",
|
||||
"../../libs/components/src/**/*.{html,ts}",
|
||||
"../../libs/auth/src/**/*.{html,ts}",
|
||||
"../../libs/angular/src/**/*.{html,ts}",
|
||||
];
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
||||
import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { HashPurpose } from "@bitwarden/common/platform/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { MasterKey } from "@bitwarden/common/types/key";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
import { ConvertToKeyConnectorCommand } from "../../commands/convert-to-key-connector.command";
|
||||
@ -26,16 +25,14 @@ export class UnlockCommand {
|
||||
private accountService: AccountService,
|
||||
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
private cryptoService: CryptoService,
|
||||
private stateService: StateService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private apiService: ApiService,
|
||||
private logService: ConsoleLogService,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private environmentService: EnvironmentService,
|
||||
private syncService: SyncService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private logout: () => Promise<void>,
|
||||
private kdfConfigService: KdfConfigService,
|
||||
) {}
|
||||
|
||||
async run(password: string, cmdOptions: Record<string, any>) {
|
||||
@ -52,62 +49,43 @@ export class UnlockCommand {
|
||||
const [userId, email] = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||
);
|
||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdfConfig);
|
||||
const storedMasterKeyHash = await firstValueFrom(
|
||||
this.masterPasswordService.masterKeyHash$(userId),
|
||||
);
|
||||
|
||||
let passwordValid = false;
|
||||
if (masterKey != null) {
|
||||
if (storedMasterKeyHash != null) {
|
||||
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(password, masterKey);
|
||||
} else {
|
||||
const serverKeyHash = await this.cryptoService.hashMasterKey(
|
||||
password,
|
||||
masterKey,
|
||||
HashPurpose.ServerAuthorization,
|
||||
);
|
||||
const request = new SecretVerificationRequest();
|
||||
request.masterPasswordHash = serverKeyHash;
|
||||
try {
|
||||
await this.apiService.postAccountVerifyPassword(request);
|
||||
passwordValid = true;
|
||||
const localKeyHash = await this.cryptoService.hashMasterKey(
|
||||
password,
|
||||
masterKey,
|
||||
HashPurpose.LocalAuthorization,
|
||||
);
|
||||
await this.masterPasswordService.setMasterKeyHash(localKeyHash, userId);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
const verification = {
|
||||
type: VerificationType.MasterPassword,
|
||||
secret: password,
|
||||
} as MasterPasswordVerification;
|
||||
|
||||
let masterKey: MasterKey;
|
||||
try {
|
||||
const response = await this.userVerificationService.verifyUserByMasterPassword(
|
||||
verification,
|
||||
userId,
|
||||
email,
|
||||
);
|
||||
masterKey = response.masterKey;
|
||||
} catch (e) {
|
||||
// verification failure throws
|
||||
return Response.error(e.message);
|
||||
}
|
||||
|
||||
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
|
||||
await this.cryptoService.setUserKey(userKey);
|
||||
|
||||
if (await this.keyConnectorService.getConvertAccountRequired()) {
|
||||
const convertToKeyConnectorCommand = new ConvertToKeyConnectorCommand(
|
||||
this.keyConnectorService,
|
||||
this.environmentService,
|
||||
this.syncService,
|
||||
this.organizationApiService,
|
||||
this.logout,
|
||||
);
|
||||
const convertResponse = await convertToKeyConnectorCommand.run();
|
||||
if (!convertResponse.success) {
|
||||
return convertResponse;
|
||||
}
|
||||
}
|
||||
|
||||
if (passwordValid) {
|
||||
await this.masterPasswordService.setMasterKey(masterKey, userId);
|
||||
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
|
||||
await this.cryptoService.setUserKey(userKey);
|
||||
|
||||
if (await this.keyConnectorService.getConvertAccountRequired()) {
|
||||
const convertToKeyConnectorCommand = new ConvertToKeyConnectorCommand(
|
||||
this.keyConnectorService,
|
||||
this.environmentService,
|
||||
this.syncService,
|
||||
this.organizationApiService,
|
||||
this.logout,
|
||||
);
|
||||
const convertResponse = await convertToKeyConnectorCommand.run();
|
||||
if (!convertResponse.success) {
|
||||
return convertResponse;
|
||||
}
|
||||
}
|
||||
|
||||
return this.successResponse();
|
||||
} else {
|
||||
return Response.error("Invalid master password.");
|
||||
}
|
||||
return this.successResponse();
|
||||
}
|
||||
|
||||
private async setNewSessionKey() {
|
||||
|
@ -140,16 +140,14 @@ export abstract class BaseProgram {
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.masterPasswordService,
|
||||
this.serviceContainer.cryptoService,
|
||||
this.serviceContainer.stateService,
|
||||
this.serviceContainer.userVerificationService,
|
||||
this.serviceContainer.cryptoFunctionService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.logService,
|
||||
this.serviceContainer.keyConnectorService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.syncService,
|
||||
this.serviceContainer.organizationApiService,
|
||||
this.serviceContainer.logout,
|
||||
this.serviceContainer.kdfConfigService,
|
||||
);
|
||||
const response = await command.run(null, null);
|
||||
if (!response.success) {
|
||||
|
@ -468,7 +468,7 @@ export class GetCommand extends DownloadCommand {
|
||||
if (Utils.isGuid(id)) {
|
||||
org = await this.organizationService.getFromState(id);
|
||||
} else if (id.trim() !== "") {
|
||||
let orgs = await this.organizationService.getAll();
|
||||
let orgs = await firstValueFrom(this.organizationService.organizations$);
|
||||
orgs = CliUtils.searchOrganizations(orgs, id);
|
||||
if (orgs.length > 1) {
|
||||
return Response.multipleResults(orgs.map((c) => c.id));
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
@ -239,7 +241,7 @@ export class ListCommand {
|
||||
}
|
||||
|
||||
private async listOrganizations(options: Options) {
|
||||
let organizations = await this.organizationService.getAll();
|
||||
let organizations = await firstValueFrom(this.organizationService.memberOrganizations$);
|
||||
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
organizations = CliUtils.searchOrganizations(organizations, options.search);
|
||||
|
@ -120,16 +120,14 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.masterPasswordService,
|
||||
this.serviceContainer.cryptoService,
|
||||
this.serviceContainer.stateService,
|
||||
this.serviceContainer.userVerificationService,
|
||||
this.serviceContainer.cryptoFunctionService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.logService,
|
||||
this.serviceContainer.keyConnectorService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.syncService,
|
||||
this.serviceContainer.organizationApiService,
|
||||
async () => await this.serviceContainer.logout(),
|
||||
this.serviceContainer.kdfConfigService,
|
||||
);
|
||||
|
||||
this.sendCreateCommand = new SendCreateCommand(
|
||||
|
@ -270,16 +270,14 @@ export class Program extends BaseProgram {
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.masterPasswordService,
|
||||
this.serviceContainer.cryptoService,
|
||||
this.serviceContainer.stateService,
|
||||
this.serviceContainer.userVerificationService,
|
||||
this.serviceContainer.cryptoFunctionService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.logService,
|
||||
this.serviceContainer.keyConnectorService,
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.syncService,
|
||||
this.serviceContainer.organizationApiService,
|
||||
async () => await this.serviceContainer.logout(),
|
||||
this.serviceContainer.kdfConfigService,
|
||||
);
|
||||
const response = await command.run(password, cmd);
|
||||
this.processResponse(response);
|
||||
|
@ -613,7 +613,6 @@ export class ServiceContainer {
|
||||
await this.cryptoService.clearStoredUserKey(KeySuffixOptions.Auto);
|
||||
|
||||
this.userVerificationService = new UserVerificationService(
|
||||
this.stateService,
|
||||
this.cryptoService,
|
||||
this.accountService,
|
||||
this.masterPasswordService,
|
||||
|
@ -24,7 +24,7 @@
|
||||
"**/node_modules/argon2/package.json",
|
||||
"**/node_modules/argon2/lib/binding/napi-v3/argon2.node"
|
||||
],
|
||||
"electronVersion": "29.4.2",
|
||||
"electronVersion": "30.1.0",
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user