diff --git a/appinfo/routes.php b/appinfo/routes.php index 9a43ac1b1..652d445da 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -468,6 +468,10 @@ // returns `{users, total, hasMore}` with offset-based pagination. ['name' => 'peopleWidget#getUsers', 'url' => '/api/people', 'verb' => 'GET'], + // Search shortcuts (search-ai-prefix-shortcuts REQ-QSP-001): admin list. + ['name' => 'searchShortcuts#index', 'url' => '/api/admin/search-shortcuts', 'verb' => 'GET'], + ['name' => 'searchShortcuts#save', 'url' => '/api/admin/search-shortcuts', 'verb' => 'PUT'], + // Custom profile fields (REQ-PEX-001, REQ-PEX-002): own values for // every signed-in person, definitions for administrators. ['name' => 'profileFields#getOwn', 'url' => '/api/profile-fields/me', 'verb' => 'GET'], diff --git a/l10n/be.js b/l10n/be.js index 4d50e737e..76286ce0d 100644 --- a/l10n/be.js +++ b/l10n/be.js @@ -1324,6 +1324,29 @@ OC.L10N.register( "Save profile fields" : "Захаваць палі профілю", "The profile fields could not be loaded." : "Не ўдалося загрузіць палі профілю.", "The profile fields could not be saved." : "Не ўдалося захаваць палі профілю.", + "Forget my usage" : "Забыць маё выкарыстанне", + "Sorted alphabetically" : "Упарадкавана па алфавіце", + "Sorted by most used" : "Упарадкавана па частаце выкарыстання", + "Sorted by last used" : "Упарадкавана па апошнім выкарыстанні", + "Sorted at random" : "Упарадкавана выпадкова", + "Sort tiles" : "Парадак плітак", + "By hand" : "Уручную", + "Alphabetically" : "Па алфавіце", + "Most used" : "Найчасцей выкарыстоўваныя", + "Last used" : "Апошнія выкарыстаныя", + "At random" : "Выпадкова", + "Search shortcuts" : "Ярлыкі пошуку", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Пошук, які пачынаецца з прэфікса, напрыклад !t printer, адразу адкрывае гэты сайт у новай укладцы. Увод ? у полі пошуку паказвае спіс ярлыкоў.", + "Prefix" : "Прэфікс", + "Address with {query}" : "Адрас з {query}", + "No search shortcuts yet." : "Ярлыкоў пошуку пакуль няма.", + "Search shortcuts saved." : "Ярлыкі пошуку захаваны.", + "Add shortcut" : "Дадаць ярлык", + "Save search shortcuts" : "Захаваць ярлыкі пошуку", + "The search shortcuts could not be loaded." : "Не ўдалося загрузіць ярлыкі пошуку.", + "The search shortcuts could not be saved." : "Не ўдалося захаваць ярлыкі пошуку.", + "Search {name} for {query}" : "Шукаць у {name}: {query}", + "Type what to search for in {name}" : "Увядзіце, што шукаць у {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/be.json b/l10n/be.json index c7b18e4e4..4dae255eb 100644 --- a/l10n/be.json +++ b/l10n/be.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Захаваць палі профілю", "The profile fields could not be loaded.": "Не ўдалося загрузіць палі профілю.", "The profile fields could not be saved.": "Не ўдалося захаваць палі профілю.", + "Forget my usage": "Забыць маё выкарыстанне", + "Sorted alphabetically": "Упарадкавана па алфавіце", + "Sorted by most used": "Упарадкавана па частаце выкарыстання", + "Sorted by last used": "Упарадкавана па апошнім выкарыстанні", + "Sorted at random": "Упарадкавана выпадкова", + "Sort tiles": "Парадак плітак", + "By hand": "Уручную", + "Alphabetically": "Па алфавіце", + "Most used": "Найчасцей выкарыстоўваныя", + "Last used": "Апошнія выкарыстаныя", + "At random": "Выпадкова", + "Search shortcuts": "Ярлыкі пошуку", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Пошук, які пачынаецца з прэфікса, напрыклад !t printer, адразу адкрывае гэты сайт у новай укладцы. Увод ? у полі пошуку паказвае спіс ярлыкоў.", + "Prefix": "Прэфікс", + "Address with {query}": "Адрас з {query}", + "No search shortcuts yet.": "Ярлыкоў пошуку пакуль няма.", + "Search shortcuts saved.": "Ярлыкі пошуку захаваны.", + "Add shortcut": "Дадаць ярлык", + "Save search shortcuts": "Захаваць ярлыкі пошуку", + "The search shortcuts could not be loaded.": "Не ўдалося загрузіць ярлыкі пошуку.", + "The search shortcuts could not be saved.": "Не ўдалося захаваць ярлыкі пошуку.", + "Search {name} for {query}": "Шукаць у {name}: {query}", + "Type what to search for in {name}": "Увядзіце, што шукаць у {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/bg.js b/l10n/bg.js index c61554189..d3b23d483 100644 --- a/l10n/bg.js +++ b/l10n/bg.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Запазване на полетата на профила", "The profile fields could not be loaded." : "Полетата на профила не можаха да се заредят.", "The profile fields could not be saved." : "Полетата на профила не можаха да се запазят.", + "Forget my usage" : "Забрави използването ми", + "Sorted alphabetically" : "Подредено по азбучен ред", + "Sorted by most used" : "Подредено по най-използвани", + "Sorted by last used" : "Подредено по последно използвани", + "Sorted at random" : "Подредено в случаен ред", + "Sort tiles" : "Подреждане на плочките", + "By hand" : "Ръчно", + "Alphabetically" : "По азбучен ред", + "Most used" : "Най-използвани", + "Last used" : "Последно използвани", + "At random" : "В случаен ред", + "Search shortcuts" : "Преки пътища за търсене", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Търсене, което започва с префикс, например !t printer, отива направо към този сайт в нов раздел. Въвеждането на ? в поле за търсене показва преките пътища.", + "Prefix" : "Префикс", + "Address with {query}" : "Адрес с {query}", + "No search shortcuts yet." : "Все още няма преки пътища за търсене.", + "Search shortcuts saved." : "Преките пътища за търсене са запазени.", + "Add shortcut" : "Добавяне на пряк път", + "Save search shortcuts" : "Запазване на преките пътища за търсене", + "The search shortcuts could not be loaded." : "Преките пътища за търсене не можаха да се заредят.", + "The search shortcuts could not be saved." : "Преките пътища за търсене не можаха да се запазят.", + "Search {name} for {query}" : "Търсене в {name} за {query}", + "Type what to search for in {name}" : "Въведете какво да търсите в {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/bg.json b/l10n/bg.json index abfde9fae..03f3650f3 100644 --- a/l10n/bg.json +++ b/l10n/bg.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Запазване на полетата на профила", "The profile fields could not be loaded.": "Полетата на профила не можаха да се заредят.", "The profile fields could not be saved.": "Полетата на профила не можаха да се запазят.", + "Forget my usage": "Забрави използването ми", + "Sorted alphabetically": "Подредено по азбучен ред", + "Sorted by most used": "Подредено по най-използвани", + "Sorted by last used": "Подредено по последно използвани", + "Sorted at random": "Подредено в случаен ред", + "Sort tiles": "Подреждане на плочките", + "By hand": "Ръчно", + "Alphabetically": "По азбучен ред", + "Most used": "Най-използвани", + "Last used": "Последно използвани", + "At random": "В случаен ред", + "Search shortcuts": "Преки пътища за търсене", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Търсене, което започва с префикс, например !t printer, отива направо към този сайт в нов раздел. Въвеждането на ? в поле за търсене показва преките пътища.", + "Prefix": "Префикс", + "Address with {query}": "Адрес с {query}", + "No search shortcuts yet.": "Все още няма преки пътища за търсене.", + "Search shortcuts saved.": "Преките пътища за търсене са запазени.", + "Add shortcut": "Добавяне на пряк път", + "Save search shortcuts": "Запазване на преките пътища за търсене", + "The search shortcuts could not be loaded.": "Преките пътища за търсене не можаха да се заредят.", + "The search shortcuts could not be saved.": "Преките пътища за търсене не можаха да се запазят.", + "Search {name} for {query}": "Търсене в {name} за {query}", + "Type what to search for in {name}": "Въведете какво да търсите в {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/bs.js b/l10n/bs.js index 15a32bcfd..f72264cbd 100644 --- a/l10n/bs.js +++ b/l10n/bs.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Spremi polja profila", "The profile fields could not be loaded." : "Polja profila nisu mogla biti učitana.", "The profile fields could not be saved." : "Polja profila nisu mogla biti spremljena.", + "Forget my usage" : "Zaboravi moje korištenje", + "Sorted alphabetically" : "Poredano abecedno", + "Sorted by most used" : "Poredano po najčešće korištenim", + "Sorted by last used" : "Poredano po zadnje korištenim", + "Sorted at random" : "Poredano nasumično", + "Sort tiles" : "Poredaj pločice", + "By hand" : "Ručno", + "Alphabetically" : "Abecedno", + "Most used" : "Najčešće korišteno", + "Last used" : "Zadnje korišteno", + "At random" : "Nasumično", + "Search shortcuts" : "Prečaci za pretragu", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Pretraga koja počinje prefiksom, npr. !t printer, ide ravno na tu stranicu u novoj kartici. Upisivanje ? u polje za pretragu prikazuje prečace.", + "Prefix" : "Prefiks", + "Address with {query}" : "Adresa s {query}", + "No search shortcuts yet." : "Još nema prečaca za pretragu.", + "Search shortcuts saved." : "Prečaci za pretragu su spremljeni.", + "Add shortcut" : "Dodaj prečac", + "Save search shortcuts" : "Spremi prečace za pretragu", + "The search shortcuts could not be loaded." : "Prečaci za pretragu nisu mogli biti učitani.", + "The search shortcuts could not be saved." : "Prečaci za pretragu nisu mogli biti spremljeni.", + "Search {name} for {query}" : "Pretraži {name} za {query}", + "Type what to search for in {name}" : "Upišite što tražite u {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/bs.json b/l10n/bs.json index 8884df768..da4838ce8 100644 --- a/l10n/bs.json +++ b/l10n/bs.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Spremi polja profila", "The profile fields could not be loaded.": "Polja profila nisu mogla biti učitana.", "The profile fields could not be saved.": "Polja profila nisu mogla biti spremljena.", + "Forget my usage": "Zaboravi moje korištenje", + "Sorted alphabetically": "Poredano abecedno", + "Sorted by most used": "Poredano po najčešće korištenim", + "Sorted by last used": "Poredano po zadnje korištenim", + "Sorted at random": "Poredano nasumično", + "Sort tiles": "Poredaj pločice", + "By hand": "Ručno", + "Alphabetically": "Abecedno", + "Most used": "Najčešće korišteno", + "Last used": "Zadnje korišteno", + "At random": "Nasumično", + "Search shortcuts": "Prečaci za pretragu", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Pretraga koja počinje prefiksom, npr. !t printer, ide ravno na tu stranicu u novoj kartici. Upisivanje ? u polje za pretragu prikazuje prečace.", + "Prefix": "Prefiks", + "Address with {query}": "Adresa s {query}", + "No search shortcuts yet.": "Još nema prečaca za pretragu.", + "Search shortcuts saved.": "Prečaci za pretragu su spremljeni.", + "Add shortcut": "Dodaj prečac", + "Save search shortcuts": "Spremi prečace za pretragu", + "The search shortcuts could not be loaded.": "Prečaci za pretragu nisu mogli biti učitani.", + "The search shortcuts could not be saved.": "Prečaci za pretragu nisu mogli biti spremljeni.", + "Search {name} for {query}": "Pretraži {name} za {query}", + "Type what to search for in {name}": "Upišite što tražite u {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ca.js b/l10n/ca.js index 20366b830..7c83f1c84 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Desa els camps del perfil", "The profile fields could not be loaded." : "No s'han pogut carregar els camps del perfil.", "The profile fields could not be saved." : "No s'han pogut desar els camps del perfil.", + "Forget my usage" : "Oblida el meu ús", + "Sorted alphabetically" : "Ordenat alfabèticament", + "Sorted by most used" : "Ordenat pels més usats", + "Sorted by last used" : "Ordenat pels darrers usats", + "Sorted at random" : "Ordenat a l'atzar", + "Sort tiles" : "Ordena les rajoles", + "By hand" : "A mà", + "Alphabetically" : "Alfabèticament", + "Most used" : "Els més usats", + "Last used" : "Darrers usats", + "At random" : "A l'atzar", + "Search shortcuts" : "Dreceres de cerca", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Una cerca que comença amb un prefix, com ara !t printer, va directament a aquell lloc en una pestanya nova. Escriure ? en un quadre de cerca mostra les dreceres.", + "Prefix" : "Prefix", + "Address with {query}" : "Adreça amb {query}", + "No search shortcuts yet." : "Encara no hi ha dreceres de cerca.", + "Search shortcuts saved." : "S'han desat les dreceres de cerca.", + "Add shortcut" : "Afegeix una drecera", + "Save search shortcuts" : "Desa les dreceres de cerca", + "The search shortcuts could not be loaded." : "No s'han pogut carregar les dreceres de cerca.", + "The search shortcuts could not be saved." : "No s'han pogut desar les dreceres de cerca.", + "Search {name} for {query}" : "Cerca {query} a {name}", + "Type what to search for in {name}" : "Escriviu què voleu cercar a {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ca.json b/l10n/ca.json index 66ddfbc64..db9feb83e 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Desa els camps del perfil", "The profile fields could not be loaded.": "No s'han pogut carregar els camps del perfil.", "The profile fields could not be saved.": "No s'han pogut desar els camps del perfil.", + "Forget my usage": "Oblida el meu ús", + "Sorted alphabetically": "Ordenat alfabèticament", + "Sorted by most used": "Ordenat pels més usats", + "Sorted by last used": "Ordenat pels darrers usats", + "Sorted at random": "Ordenat a l'atzar", + "Sort tiles": "Ordena les rajoles", + "By hand": "A mà", + "Alphabetically": "Alfabèticament", + "Most used": "Els més usats", + "Last used": "Darrers usats", + "At random": "A l'atzar", + "Search shortcuts": "Dreceres de cerca", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Una cerca que comença amb un prefix, com ara !t printer, va directament a aquell lloc en una pestanya nova. Escriure ? en un quadre de cerca mostra les dreceres.", + "Prefix": "Prefix", + "Address with {query}": "Adreça amb {query}", + "No search shortcuts yet.": "Encara no hi ha dreceres de cerca.", + "Search shortcuts saved.": "S'han desat les dreceres de cerca.", + "Add shortcut": "Afegeix una drecera", + "Save search shortcuts": "Desa les dreceres de cerca", + "The search shortcuts could not be loaded.": "No s'han pogut carregar les dreceres de cerca.", + "The search shortcuts could not be saved.": "No s'han pogut desar les dreceres de cerca.", + "Search {name} for {query}": "Cerca {query} a {name}", + "Type what to search for in {name}": "Escriviu què voleu cercar a {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/cs.js b/l10n/cs.js index ab43c2cc7..e2db2761a 100644 --- a/l10n/cs.js +++ b/l10n/cs.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Uložit pole profilu", "The profile fields could not be loaded." : "Pole profilu se nepodařilo načíst.", "The profile fields could not be saved." : "Pole profilu se nepodařilo uložit.", + "Forget my usage" : "Zapomenout mé používání", + "Sorted alphabetically" : "Seřazeno abecedně", + "Sorted by most used" : "Seřazeno podle nejpoužívanějších", + "Sorted by last used" : "Seřazeno podle naposledy použitých", + "Sorted at random" : "Seřazeno náhodně", + "Sort tiles" : "Řazení dlaždic", + "By hand" : "Ručně", + "Alphabetically" : "Abecedně", + "Most used" : "Nejpoužívanější", + "Last used" : "Naposledy použité", + "At random" : "Náhodně", + "Search shortcuts" : "Zkratky vyhledávání", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Hledání začínající předponou, například !t printer, přejde rovnou na daný web v nové kartě. Napsáním ? do vyhledávacího pole zobrazíte zkratky.", + "Prefix" : "Předpona", + "Address with {query}" : "Adresa s {query}", + "No search shortcuts yet." : "Zatím žádné zkratky vyhledávání.", + "Search shortcuts saved." : "Zkratky vyhledávání jsou uloženy.", + "Add shortcut" : "Přidat zkratku", + "Save search shortcuts" : "Uložit zkratky vyhledávání", + "The search shortcuts could not be loaded." : "Zkratky vyhledávání se nepodařilo načíst.", + "The search shortcuts could not be saved." : "Zkratky vyhledávání se nepodařilo uložit.", + "Search {name} for {query}" : "Hledat {query} v {name}", + "Type what to search for in {name}" : "Napište, co hledat v {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/cs.json b/l10n/cs.json index 8b3a4f5cc..4c0a639e6 100644 --- a/l10n/cs.json +++ b/l10n/cs.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Uložit pole profilu", "The profile fields could not be loaded.": "Pole profilu se nepodařilo načíst.", "The profile fields could not be saved.": "Pole profilu se nepodařilo uložit.", + "Forget my usage": "Zapomenout mé používání", + "Sorted alphabetically": "Seřazeno abecedně", + "Sorted by most used": "Seřazeno podle nejpoužívanějších", + "Sorted by last used": "Seřazeno podle naposledy použitých", + "Sorted at random": "Seřazeno náhodně", + "Sort tiles": "Řazení dlaždic", + "By hand": "Ručně", + "Alphabetically": "Abecedně", + "Most used": "Nejpoužívanější", + "Last used": "Naposledy použité", + "At random": "Náhodně", + "Search shortcuts": "Zkratky vyhledávání", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Hledání začínající předponou, například !t printer, přejde rovnou na daný web v nové kartě. Napsáním ? do vyhledávacího pole zobrazíte zkratky.", + "Prefix": "Předpona", + "Address with {query}": "Adresa s {query}", + "No search shortcuts yet.": "Zatím žádné zkratky vyhledávání.", + "Search shortcuts saved.": "Zkratky vyhledávání jsou uloženy.", + "Add shortcut": "Přidat zkratku", + "Save search shortcuts": "Uložit zkratky vyhledávání", + "The search shortcuts could not be loaded.": "Zkratky vyhledávání se nepodařilo načíst.", + "The search shortcuts could not be saved.": "Zkratky vyhledávání se nepodařilo uložit.", + "Search {name} for {query}": "Hledat {query} v {name}", + "Type what to search for in {name}": "Napište, co hledat v {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/da.js b/l10n/da.js index 81f69cdab..4d842702b 100644 --- a/l10n/da.js +++ b/l10n/da.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Gem profilfelter", "The profile fields could not be loaded." : "Profilfelterne kunne ikke indlæses.", "The profile fields could not be saved." : "Profilfelterne kunne ikke gemmes.", + "Forget my usage" : "Glem min brug", + "Sorted alphabetically" : "Sorteret alfabetisk", + "Sorted by most used" : "Sorteret efter mest brugte", + "Sorted by last used" : "Sorteret efter senest brugte", + "Sorted at random" : "Sorteret tilfældigt", + "Sort tiles" : "Sortér felter", + "By hand" : "Manuelt", + "Alphabetically" : "Alfabetisk", + "Most used" : "Mest brugte", + "Last used" : "Senest brugte", + "At random" : "Tilfældigt", + "Search shortcuts" : "Søgegenveje", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "En søgning, der starter med et præfiks, fx !t printer, går direkte til det websted i en ny fane. Skriv ? i et søgefelt for at se genvejene.", + "Prefix" : "Præfiks", + "Address with {query}" : "Adresse med {query}", + "No search shortcuts yet." : "Ingen søgegenveje endnu.", + "Search shortcuts saved." : "Søgegenvejene er gemt.", + "Add shortcut" : "Tilføj genvej", + "Save search shortcuts" : "Gem søgegenveje", + "The search shortcuts could not be loaded." : "Søgegenvejene kunne ikke indlæses.", + "The search shortcuts could not be saved." : "Søgegenvejene kunne ikke gemmes.", + "Search {name} for {query}" : "Søg i {name} efter {query}", + "Type what to search for in {name}" : "Skriv, hvad du vil søge efter i {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/da.json b/l10n/da.json index 73fecc45d..5271a40ca 100644 --- a/l10n/da.json +++ b/l10n/da.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Gem profilfelter", "The profile fields could not be loaded.": "Profilfelterne kunne ikke indlæses.", "The profile fields could not be saved.": "Profilfelterne kunne ikke gemmes.", + "Forget my usage": "Glem min brug", + "Sorted alphabetically": "Sorteret alfabetisk", + "Sorted by most used": "Sorteret efter mest brugte", + "Sorted by last used": "Sorteret efter senest brugte", + "Sorted at random": "Sorteret tilfældigt", + "Sort tiles": "Sortér felter", + "By hand": "Manuelt", + "Alphabetically": "Alfabetisk", + "Most used": "Mest brugte", + "Last used": "Senest brugte", + "At random": "Tilfældigt", + "Search shortcuts": "Søgegenveje", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "En søgning, der starter med et præfiks, fx !t printer, går direkte til det websted i en ny fane. Skriv ? i et søgefelt for at se genvejene.", + "Prefix": "Præfiks", + "Address with {query}": "Adresse med {query}", + "No search shortcuts yet.": "Ingen søgegenveje endnu.", + "Search shortcuts saved.": "Søgegenvejene er gemt.", + "Add shortcut": "Tilføj genvej", + "Save search shortcuts": "Gem søgegenveje", + "The search shortcuts could not be loaded.": "Søgegenvejene kunne ikke indlæses.", + "The search shortcuts could not be saved.": "Søgegenvejene kunne ikke gemmes.", + "Search {name} for {query}": "Søg i {name} efter {query}", + "Type what to search for in {name}": "Skriv, hvad du vil søge efter i {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/de.js b/l10n/de.js index 62b638d0c..bff2f8069 100644 --- a/l10n/de.js +++ b/l10n/de.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Profilfelder speichern", "The profile fields could not be loaded." : "Die Profilfelder konnten nicht geladen werden.", "The profile fields could not be saved." : "Die Profilfelder konnten nicht gespeichert werden.", + "Forget my usage" : "Meine Nutzung vergessen", + "Sorted alphabetically" : "Alphabetisch sortiert", + "Sorted by most used" : "Nach häufigster Nutzung sortiert", + "Sorted by last used" : "Nach letzter Nutzung sortiert", + "Sorted at random" : "Zufällig sortiert", + "Sort tiles" : "Kacheln sortieren", + "By hand" : "Von Hand", + "Alphabetically" : "Alphabetisch", + "Most used" : "Am häufigsten genutzt", + "Last used" : "Zuletzt genutzt", + "At random" : "Zufällig", + "Search shortcuts" : "Suchkürzel", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Eine Suche, die mit einem Präfix beginnt, etwa !t printer, geht direkt in einem neuen Tab zu dieser Seite. Ein ? im Suchfeld listet die Kürzel auf.", + "Prefix" : "Präfix", + "Address with {query}" : "Adresse mit {query}", + "No search shortcuts yet." : "Noch keine Suchkürzel.", + "Search shortcuts saved." : "Suchkürzel gespeichert.", + "Add shortcut" : "Kürzel hinzufügen", + "Save search shortcuts" : "Suchkürzel speichern", + "The search shortcuts could not be loaded." : "Die Suchkürzel konnten nicht geladen werden.", + "The search shortcuts could not be saved." : "Die Suchkürzel konnten nicht gespeichert werden.", + "Search {name} for {query}" : "In {name} nach {query} suchen", + "Type what to search for in {name}" : "Gib ein, wonach in {name} gesucht werden soll", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/de.json b/l10n/de.json index ae82a5a14..a53b02aad 100644 --- a/l10n/de.json +++ b/l10n/de.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Profilfelder speichern", "The profile fields could not be loaded.": "Die Profilfelder konnten nicht geladen werden.", "The profile fields could not be saved.": "Die Profilfelder konnten nicht gespeichert werden.", + "Forget my usage": "Meine Nutzung vergessen", + "Sorted alphabetically": "Alphabetisch sortiert", + "Sorted by most used": "Nach häufigster Nutzung sortiert", + "Sorted by last used": "Nach letzter Nutzung sortiert", + "Sorted at random": "Zufällig sortiert", + "Sort tiles": "Kacheln sortieren", + "By hand": "Von Hand", + "Alphabetically": "Alphabetisch", + "Most used": "Am häufigsten genutzt", + "Last used": "Zuletzt genutzt", + "At random": "Zufällig", + "Search shortcuts": "Suchkürzel", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Eine Suche, die mit einem Präfix beginnt, etwa !t printer, geht direkt in einem neuen Tab zu dieser Seite. Ein ? im Suchfeld listet die Kürzel auf.", + "Prefix": "Präfix", + "Address with {query}": "Adresse mit {query}", + "No search shortcuts yet.": "Noch keine Suchkürzel.", + "Search shortcuts saved.": "Suchkürzel gespeichert.", + "Add shortcut": "Kürzel hinzufügen", + "Save search shortcuts": "Suchkürzel speichern", + "The search shortcuts could not be loaded.": "Die Suchkürzel konnten nicht geladen werden.", + "The search shortcuts could not be saved.": "Die Suchkürzel konnten nicht gespeichert werden.", + "Search {name} for {query}": "In {name} nach {query} suchen", + "Type what to search for in {name}": "Gib ein, wonach in {name} gesucht werden soll", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/el.js b/l10n/el.js index 4c504866f..5141426b5 100644 --- a/l10n/el.js +++ b/l10n/el.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Αποθήκευση πεδίων προφίλ", "The profile fields could not be loaded." : "Δεν ήταν δυνατή η φόρτωση των πεδίων προφίλ.", "The profile fields could not be saved." : "Δεν ήταν δυνατή η αποθήκευση των πεδίων προφίλ.", + "Forget my usage" : "Διαγραφή της χρήσης μου", + "Sorted alphabetically" : "Ταξινομημένα αλφαβητικά", + "Sorted by most used" : "Ταξινομημένα κατά συχνότερη χρήση", + "Sorted by last used" : "Ταξινομημένα κατά τελευταία χρήση", + "Sorted at random" : "Ταξινομημένα τυχαία", + "Sort tiles" : "Ταξινόμηση πλακιδίων", + "By hand" : "Χειροκίνητα", + "Alphabetically" : "Αλφαβητικά", + "Most used" : "Συχνότερα χρησιμοποιούμενα", + "Last used" : "Τελευταία χρήση", + "At random" : "Τυχαία", + "Search shortcuts" : "Συντομεύσεις αναζήτησης", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Μια αναζήτηση που ξεκινά με πρόθεμα, όπως !t printer, πηγαίνει κατευθείαν σε εκείνη την τοποθεσία σε νέα καρτέλα. Πληκτρολογώντας ? σε ένα πεδίο αναζήτησης εμφανίζονται οι συντομεύσεις.", + "Prefix" : "Πρόθεμα", + "Address with {query}" : "Διεύθυνση με {query}", + "No search shortcuts yet." : "Δεν υπάρχουν ακόμα συντομεύσεις αναζήτησης.", + "Search shortcuts saved." : "Οι συντομεύσεις αναζήτησης αποθηκεύτηκαν.", + "Add shortcut" : "Προσθήκη συντόμευσης", + "Save search shortcuts" : "Αποθήκευση συντομεύσεων αναζήτησης", + "The search shortcuts could not be loaded." : "Δεν ήταν δυνατή η φόρτωση των συντομεύσεων αναζήτησης.", + "The search shortcuts could not be saved." : "Δεν ήταν δυνατή η αποθήκευση των συντομεύσεων αναζήτησης.", + "Search {name} for {query}" : "Αναζήτηση στο {name} για {query}", + "Type what to search for in {name}" : "Πληκτρολογήστε τι να αναζητηθεί στο {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/el.json b/l10n/el.json index c7adb5b02..a7b44342f 100644 --- a/l10n/el.json +++ b/l10n/el.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Αποθήκευση πεδίων προφίλ", "The profile fields could not be loaded.": "Δεν ήταν δυνατή η φόρτωση των πεδίων προφίλ.", "The profile fields could not be saved.": "Δεν ήταν δυνατή η αποθήκευση των πεδίων προφίλ.", + "Forget my usage": "Διαγραφή της χρήσης μου", + "Sorted alphabetically": "Ταξινομημένα αλφαβητικά", + "Sorted by most used": "Ταξινομημένα κατά συχνότερη χρήση", + "Sorted by last used": "Ταξινομημένα κατά τελευταία χρήση", + "Sorted at random": "Ταξινομημένα τυχαία", + "Sort tiles": "Ταξινόμηση πλακιδίων", + "By hand": "Χειροκίνητα", + "Alphabetically": "Αλφαβητικά", + "Most used": "Συχνότερα χρησιμοποιούμενα", + "Last used": "Τελευταία χρήση", + "At random": "Τυχαία", + "Search shortcuts": "Συντομεύσεις αναζήτησης", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Μια αναζήτηση που ξεκινά με πρόθεμα, όπως !t printer, πηγαίνει κατευθείαν σε εκείνη την τοποθεσία σε νέα καρτέλα. Πληκτρολογώντας ? σε ένα πεδίο αναζήτησης εμφανίζονται οι συντομεύσεις.", + "Prefix": "Πρόθεμα", + "Address with {query}": "Διεύθυνση με {query}", + "No search shortcuts yet.": "Δεν υπάρχουν ακόμα συντομεύσεις αναζήτησης.", + "Search shortcuts saved.": "Οι συντομεύσεις αναζήτησης αποθηκεύτηκαν.", + "Add shortcut": "Προσθήκη συντόμευσης", + "Save search shortcuts": "Αποθήκευση συντομεύσεων αναζήτησης", + "The search shortcuts could not be loaded.": "Δεν ήταν δυνατή η φόρτωση των συντομεύσεων αναζήτησης.", + "The search shortcuts could not be saved.": "Δεν ήταν δυνατή η αποθήκευση των συντομεύσεων αναζήτησης.", + "Search {name} for {query}": "Αναζήτηση στο {name} για {query}", + "Type what to search for in {name}": "Πληκτρολογήστε τι να αναζητηθεί στο {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/en.js b/l10n/en.js index 5d440b4ed..c72ddde3f 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -1313,6 +1313,29 @@ OC.L10N.register( "Save profile fields" : "Save profile fields", "The profile fields could not be loaded." : "The profile fields could not be loaded.", "The profile fields could not be saved." : "The profile fields could not be saved.", + "Forget my usage" : "Forget my usage", + "Sorted alphabetically" : "Sorted alphabetically", + "Sorted by most used" : "Sorted by most used", + "Sorted by last used" : "Sorted by last used", + "Sorted at random" : "Sorted at random", + "Sort tiles" : "Sort tiles", + "By hand" : "By hand", + "Alphabetically" : "Alphabetically", + "Most used" : "Most used", + "Last used" : "Last used", + "At random" : "At random", + "Search shortcuts" : "Search shortcuts", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.", + "Prefix" : "Prefix", + "Address with {query}" : "Address with {query}", + "No search shortcuts yet." : "No search shortcuts yet.", + "Search shortcuts saved." : "Search shortcuts saved.", + "Add shortcut" : "Add shortcut", + "Save search shortcuts" : "Save search shortcuts", + "The search shortcuts could not be loaded." : "The search shortcuts could not be loaded.", + "The search shortcuts could not be saved." : "The search shortcuts could not be saved.", + "Search {name} for {query}" : "Search {name} for {query}", + "Type what to search for in {name}" : "Type what to search for in {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/en.json b/l10n/en.json index e641b08a0..a20819489 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -1278,6 +1278,29 @@ "Save profile fields": "Save profile fields", "The profile fields could not be loaded.": "The profile fields could not be loaded.", "The profile fields could not be saved.": "The profile fields could not be saved.", + "Forget my usage": "Forget my usage", + "Sorted alphabetically": "Sorted alphabetically", + "Sorted by most used": "Sorted by most used", + "Sorted by last used": "Sorted by last used", + "Sorted at random": "Sorted at random", + "Sort tiles": "Sort tiles", + "By hand": "By hand", + "Alphabetically": "Alphabetically", + "Most used": "Most used", + "Last used": "Last used", + "At random": "At random", + "Search shortcuts": "Search shortcuts", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.", + "Prefix": "Prefix", + "Address with {query}": "Address with {query}", + "No search shortcuts yet.": "No search shortcuts yet.", + "Search shortcuts saved.": "Search shortcuts saved.", + "Add shortcut": "Add shortcut", + "Save search shortcuts": "Save search shortcuts", + "The search shortcuts could not be loaded.": "The search shortcuts could not be loaded.", + "The search shortcuts could not be saved.": "The search shortcuts could not be saved.", + "Search {name} for {query}": "Search {name} for {query}", + "Type what to search for in {name}": "Type what to search for in {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/es.js b/l10n/es.js index 6f7a0b8bc..eef69f6bd 100644 --- a/l10n/es.js +++ b/l10n/es.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Guardar campos del perfil", "The profile fields could not be loaded." : "No se pudieron cargar los campos del perfil.", "The profile fields could not be saved." : "No se pudieron guardar los campos del perfil.", + "Forget my usage" : "Olvidar mi uso", + "Sorted alphabetically" : "Ordenado alfabéticamente", + "Sorted by most used" : "Ordenado por más usados", + "Sorted by last used" : "Ordenado por último uso", + "Sorted at random" : "Ordenado al azar", + "Sort tiles" : "Ordenar mosaicos", + "By hand" : "A mano", + "Alphabetically" : "Alfabéticamente", + "Most used" : "Más usados", + "Last used" : "Último uso", + "At random" : "Al azar", + "Search shortcuts" : "Atajos de búsqueda", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Una búsqueda que empieza con un prefijo, como !t printer, va directamente a ese sitio en una pestaña nueva. Escribir ? en un cuadro de búsqueda muestra los atajos.", + "Prefix" : "Prefijo", + "Address with {query}" : "Dirección con {query}", + "No search shortcuts yet." : "Aún no hay atajos de búsqueda.", + "Search shortcuts saved." : "Atajos de búsqueda guardados.", + "Add shortcut" : "Añadir atajo", + "Save search shortcuts" : "Guardar atajos de búsqueda", + "The search shortcuts could not be loaded." : "No se pudieron cargar los atajos de búsqueda.", + "The search shortcuts could not be saved." : "No se pudieron guardar los atajos de búsqueda.", + "Search {name} for {query}" : "Buscar {query} en {name}", + "Type what to search for in {name}" : "Escribe qué buscar en {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/es.json b/l10n/es.json index 013e9ec42..fc45d4b9f 100644 --- a/l10n/es.json +++ b/l10n/es.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Guardar campos del perfil", "The profile fields could not be loaded.": "No se pudieron cargar los campos del perfil.", "The profile fields could not be saved.": "No se pudieron guardar los campos del perfil.", + "Forget my usage": "Olvidar mi uso", + "Sorted alphabetically": "Ordenado alfabéticamente", + "Sorted by most used": "Ordenado por más usados", + "Sorted by last used": "Ordenado por último uso", + "Sorted at random": "Ordenado al azar", + "Sort tiles": "Ordenar mosaicos", + "By hand": "A mano", + "Alphabetically": "Alfabéticamente", + "Most used": "Más usados", + "Last used": "Último uso", + "At random": "Al azar", + "Search shortcuts": "Atajos de búsqueda", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Una búsqueda que empieza con un prefijo, como !t printer, va directamente a ese sitio en una pestaña nueva. Escribir ? en un cuadro de búsqueda muestra los atajos.", + "Prefix": "Prefijo", + "Address with {query}": "Dirección con {query}", + "No search shortcuts yet.": "Aún no hay atajos de búsqueda.", + "Search shortcuts saved.": "Atajos de búsqueda guardados.", + "Add shortcut": "Añadir atajo", + "Save search shortcuts": "Guardar atajos de búsqueda", + "The search shortcuts could not be loaded.": "No se pudieron cargar los atajos de búsqueda.", + "The search shortcuts could not be saved.": "No se pudieron guardar los atajos de búsqueda.", + "Search {name} for {query}": "Buscar {query} en {name}", + "Type what to search for in {name}": "Escribe qué buscar en {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/et.js b/l10n/et.js index efc18db0d..c4a828cee 100644 --- a/l10n/et.js +++ b/l10n/et.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Salvesta profiili väljad", "The profile fields could not be loaded." : "Profiili välju ei õnnestunud laadida.", "The profile fields could not be saved." : "Profiili välju ei õnnestunud salvestada.", + "Forget my usage" : "Unusta minu kasutus", + "Sorted alphabetically" : "Sorditud tähestiku järgi", + "Sorted by most used" : "Sorditud enim kasutatu järgi", + "Sorted by last used" : "Sorditud viimati kasutatu järgi", + "Sorted at random" : "Sorditud juhuslikult", + "Sort tiles" : "Paanide järjestus", + "By hand" : "Käsitsi", + "Alphabetically" : "Tähestiku järgi", + "Most used" : "Enim kasutatud", + "Last used" : "Viimati kasutatud", + "At random" : "Juhuslikult", + "Search shortcuts" : "Otsingu otseteed", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Eesliitega algav otsing, näiteks !t printer, viib otse sellele saidile uuel vahekaardil. Sisesta otsingukasti ?, et näha otseteid.", + "Prefix" : "Eesliide", + "Address with {query}" : "Aadress koos {query}", + "No search shortcuts yet." : "Otsingu otseteid veel pole.", + "Search shortcuts saved." : "Otsingu otseteed salvestatud.", + "Add shortcut" : "Lisa otsetee", + "Save search shortcuts" : "Salvesta otsingu otseteed", + "The search shortcuts could not be loaded." : "Otsingu otseteid ei õnnestunud laadida.", + "The search shortcuts could not be saved." : "Otsingu otseteid ei õnnestunud salvestada.", + "Search {name} for {query}" : "Otsi {name} kaudu: {query}", + "Type what to search for in {name}" : "Sisesta, mida {name} kaudu otsida", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/et.json b/l10n/et.json index 95c5f48ec..2f155045e 100644 --- a/l10n/et.json +++ b/l10n/et.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Salvesta profiili väljad", "The profile fields could not be loaded.": "Profiili välju ei õnnestunud laadida.", "The profile fields could not be saved.": "Profiili välju ei õnnestunud salvestada.", + "Forget my usage": "Unusta minu kasutus", + "Sorted alphabetically": "Sorditud tähestiku järgi", + "Sorted by most used": "Sorditud enim kasutatu järgi", + "Sorted by last used": "Sorditud viimati kasutatu järgi", + "Sorted at random": "Sorditud juhuslikult", + "Sort tiles": "Paanide järjestus", + "By hand": "Käsitsi", + "Alphabetically": "Tähestiku järgi", + "Most used": "Enim kasutatud", + "Last used": "Viimati kasutatud", + "At random": "Juhuslikult", + "Search shortcuts": "Otsingu otseteed", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Eesliitega algav otsing, näiteks !t printer, viib otse sellele saidile uuel vahekaardil. Sisesta otsingukasti ?, et näha otseteid.", + "Prefix": "Eesliide", + "Address with {query}": "Aadress koos {query}", + "No search shortcuts yet.": "Otsingu otseteid veel pole.", + "Search shortcuts saved.": "Otsingu otseteed salvestatud.", + "Add shortcut": "Lisa otsetee", + "Save search shortcuts": "Salvesta otsingu otseteed", + "The search shortcuts could not be loaded.": "Otsingu otseteid ei õnnestunud laadida.", + "The search shortcuts could not be saved.": "Otsingu otseteid ei õnnestunud salvestada.", + "Search {name} for {query}": "Otsi {name} kaudu: {query}", + "Type what to search for in {name}": "Sisesta, mida {name} kaudu otsida", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/fi.js b/l10n/fi.js index 79d9f0757..d25e675be 100644 --- a/l10n/fi.js +++ b/l10n/fi.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Tallenna profiilikentät", "The profile fields could not be loaded." : "Profiilikenttiä ei voitu ladata.", "The profile fields could not be saved." : "Profiilikenttiä ei voitu tallentaa.", + "Forget my usage" : "Unohda käyttöni", + "Sorted alphabetically" : "Järjestetty aakkosjärjestykseen", + "Sorted by most used" : "Järjestetty eniten käytetyn mukaan", + "Sorted by last used" : "Järjestetty viimeksi käytetyn mukaan", + "Sorted at random" : "Järjestetty satunnaisesti", + "Sort tiles" : "Ruutujen järjestys", + "By hand" : "Käsin", + "Alphabetically" : "Aakkosjärjestyksessä", + "Most used" : "Eniten käytetyt", + "Last used" : "Viimeksi käytetyt", + "At random" : "Satunnaisesti", + "Search shortcuts" : "Hakuoikotiet", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Etuliitteellä alkava haku, kuten !t printer, avaa kyseisen sivuston suoraan uuteen välilehteen. Kirjoita hakukenttään ?, niin näet oikotiet.", + "Prefix" : "Etuliite", + "Address with {query}" : "Osoite, jossa {query}", + "No search shortcuts yet." : "Hakuoikoteitä ei vielä ole.", + "Search shortcuts saved." : "Hakuoikotiet tallennettu.", + "Add shortcut" : "Lisää oikotie", + "Save search shortcuts" : "Tallenna hakuoikotiet", + "The search shortcuts could not be loaded." : "Hakuoikoteitä ei voitu ladata.", + "The search shortcuts could not be saved." : "Hakuoikoteitä ei voitu tallentaa.", + "Search {name} for {query}" : "Hae {name}-palvelusta: {query}", + "Type what to search for in {name}" : "Kirjoita, mitä haetaan palvelusta {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/fi.json b/l10n/fi.json index 6c44f2272..1124822c3 100644 --- a/l10n/fi.json +++ b/l10n/fi.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Tallenna profiilikentät", "The profile fields could not be loaded.": "Profiilikenttiä ei voitu ladata.", "The profile fields could not be saved.": "Profiilikenttiä ei voitu tallentaa.", + "Forget my usage": "Unohda käyttöni", + "Sorted alphabetically": "Järjestetty aakkosjärjestykseen", + "Sorted by most used": "Järjestetty eniten käytetyn mukaan", + "Sorted by last used": "Järjestetty viimeksi käytetyn mukaan", + "Sorted at random": "Järjestetty satunnaisesti", + "Sort tiles": "Ruutujen järjestys", + "By hand": "Käsin", + "Alphabetically": "Aakkosjärjestyksessä", + "Most used": "Eniten käytetyt", + "Last used": "Viimeksi käytetyt", + "At random": "Satunnaisesti", + "Search shortcuts": "Hakuoikotiet", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Etuliitteellä alkava haku, kuten !t printer, avaa kyseisen sivuston suoraan uuteen välilehteen. Kirjoita hakukenttään ?, niin näet oikotiet.", + "Prefix": "Etuliite", + "Address with {query}": "Osoite, jossa {query}", + "No search shortcuts yet.": "Hakuoikoteitä ei vielä ole.", + "Search shortcuts saved.": "Hakuoikotiet tallennettu.", + "Add shortcut": "Lisää oikotie", + "Save search shortcuts": "Tallenna hakuoikotiet", + "The search shortcuts could not be loaded.": "Hakuoikoteitä ei voitu ladata.", + "The search shortcuts could not be saved.": "Hakuoikoteitä ei voitu tallentaa.", + "Search {name} for {query}": "Hae {name}-palvelusta: {query}", + "Type what to search for in {name}": "Kirjoita, mitä haetaan palvelusta {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/fr.js b/l10n/fr.js index b03d38fd4..ba471bdb3 100644 --- a/l10n/fr.js +++ b/l10n/fr.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Enregistrer les champs du profil", "The profile fields could not be loaded." : "Les champs du profil n'ont pas pu être chargés.", "The profile fields could not be saved." : "Les champs du profil n'ont pas pu être enregistrés.", + "Forget my usage" : "Oublier mon utilisation", + "Sorted alphabetically" : "Trié par ordre alphabétique", + "Sorted by most used" : "Trié par les plus utilisés", + "Sorted by last used" : "Trié par dernière utilisation", + "Sorted at random" : "Trié au hasard", + "Sort tiles" : "Trier les tuiles", + "By hand" : "À la main", + "Alphabetically" : "Par ordre alphabétique", + "Most used" : "Les plus utilisés", + "Last used" : "Dernière utilisation", + "At random" : "Au hasard", + "Search shortcuts" : "Raccourcis de recherche", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Une recherche qui commence par un préfixe, comme !t printer, va directement sur ce site dans un nouvel onglet. Saisir ? dans un champ de recherche affiche les raccourcis.", + "Prefix" : "Préfixe", + "Address with {query}" : "Adresse avec {query}", + "No search shortcuts yet." : "Aucun raccourci de recherche pour l'instant.", + "Search shortcuts saved." : "Raccourcis de recherche enregistrés.", + "Add shortcut" : "Ajouter un raccourci", + "Save search shortcuts" : "Enregistrer les raccourcis de recherche", + "The search shortcuts could not be loaded." : "Les raccourcis de recherche n'ont pas pu être chargés.", + "The search shortcuts could not be saved." : "Les raccourcis de recherche n'ont pas pu être enregistrés.", + "Search {name} for {query}" : "Rechercher {query} dans {name}", + "Type what to search for in {name}" : "Saisissez ce que vous cherchez dans {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/fr.json b/l10n/fr.json index 691ce24fa..eb004da1d 100644 --- a/l10n/fr.json +++ b/l10n/fr.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Enregistrer les champs du profil", "The profile fields could not be loaded.": "Les champs du profil n'ont pas pu être chargés.", "The profile fields could not be saved.": "Les champs du profil n'ont pas pu être enregistrés.", + "Forget my usage": "Oublier mon utilisation", + "Sorted alphabetically": "Trié par ordre alphabétique", + "Sorted by most used": "Trié par les plus utilisés", + "Sorted by last used": "Trié par dernière utilisation", + "Sorted at random": "Trié au hasard", + "Sort tiles": "Trier les tuiles", + "By hand": "À la main", + "Alphabetically": "Par ordre alphabétique", + "Most used": "Les plus utilisés", + "Last used": "Dernière utilisation", + "At random": "Au hasard", + "Search shortcuts": "Raccourcis de recherche", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Une recherche qui commence par un préfixe, comme !t printer, va directement sur ce site dans un nouvel onglet. Saisir ? dans un champ de recherche affiche les raccourcis.", + "Prefix": "Préfixe", + "Address with {query}": "Adresse avec {query}", + "No search shortcuts yet.": "Aucun raccourci de recherche pour l'instant.", + "Search shortcuts saved.": "Raccourcis de recherche enregistrés.", + "Add shortcut": "Ajouter un raccourci", + "Save search shortcuts": "Enregistrer les raccourcis de recherche", + "The search shortcuts could not be loaded.": "Les raccourcis de recherche n'ont pas pu être chargés.", + "The search shortcuts could not be saved.": "Les raccourcis de recherche n'ont pas pu être enregistrés.", + "Search {name} for {query}": "Rechercher {query} dans {name}", + "Type what to search for in {name}": "Saisissez ce que vous cherchez dans {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ga.js b/l10n/ga.js index 88fd7a088..acd6b6af9 100644 --- a/l10n/ga.js +++ b/l10n/ga.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Sábháil réimsí próifíle", "The profile fields could not be loaded." : "Níorbh fhéidir na réimsí próifíle a lódáil.", "The profile fields could not be saved." : "Níorbh fhéidir na réimsí próifíle a shábháil.", + "Forget my usage" : "Déan dearmad ar m'úsáid", + "Sorted alphabetically" : "Sórtáilte in ord aibítre", + "Sorted by most used" : "Sórtáilte de réir is mó a úsáidtear", + "Sorted by last used" : "Sórtáilte de réir an úsáid is déanaí", + "Sorted at random" : "Sórtáilte go randamach", + "Sort tiles" : "Sórtáil tíleanna", + "By hand" : "De láimh", + "Alphabetically" : "In ord aibítre", + "Most used" : "Is mó a úsáidtear", + "Last used" : "Úsáidte is déanaí", + "At random" : "Go randamach", + "Search shortcuts" : "Aicearraí cuardaigh", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Téann cuardach a thosaíonn le réimír, mar shampla !t printer, díreach chuig an suíomh sin i gcluaisín nua. Clóscríobh ? i mbosca cuardaigh chun na haicearraí a liostú.", + "Prefix" : "Réimír", + "Address with {query}" : "Seoladh le {query}", + "No search shortcuts yet." : "Níl aon aicearraí cuardaigh fós.", + "Search shortcuts saved." : "Aicearraí cuardaigh sábháilte.", + "Add shortcut" : "Cuir aicearra leis", + "Save search shortcuts" : "Sábháil aicearraí cuardaigh", + "The search shortcuts could not be loaded." : "Níorbh fhéidir na haicearraí cuardaigh a lódáil.", + "The search shortcuts could not be saved." : "Níorbh fhéidir na haicearraí cuardaigh a shábháil.", + "Search {name} for {query}" : "Cuardaigh {name} le haghaidh {query}", + "Type what to search for in {name}" : "Clóscríobh cad atá le cuardach in {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ga.json b/l10n/ga.json index f48ae6820..5b0df0451 100644 --- a/l10n/ga.json +++ b/l10n/ga.json @@ -1303,6 +1303,29 @@ "Save profile fields": "Sábháil réimsí próifíle", "The profile fields could not be loaded.": "Níorbh fhéidir na réimsí próifíle a lódáil.", "The profile fields could not be saved.": "Níorbh fhéidir na réimsí próifíle a shábháil.", + "Forget my usage": "Déan dearmad ar m'úsáid", + "Sorted alphabetically": "Sórtáilte in ord aibítre", + "Sorted by most used": "Sórtáilte de réir is mó a úsáidtear", + "Sorted by last used": "Sórtáilte de réir an úsáid is déanaí", + "Sorted at random": "Sórtáilte go randamach", + "Sort tiles": "Sórtáil tíleanna", + "By hand": "De láimh", + "Alphabetically": "In ord aibítre", + "Most used": "Is mó a úsáidtear", + "Last used": "Úsáidte is déanaí", + "At random": "Go randamach", + "Search shortcuts": "Aicearraí cuardaigh", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Téann cuardach a thosaíonn le réimír, mar shampla !t printer, díreach chuig an suíomh sin i gcluaisín nua. Clóscríobh ? i mbosca cuardaigh chun na haicearraí a liostú.", + "Prefix": "Réimír", + "Address with {query}": "Seoladh le {query}", + "No search shortcuts yet.": "Níl aon aicearraí cuardaigh fós.", + "Search shortcuts saved.": "Aicearraí cuardaigh sábháilte.", + "Add shortcut": "Cuir aicearra leis", + "Save search shortcuts": "Sábháil aicearraí cuardaigh", + "The search shortcuts could not be loaded.": "Níorbh fhéidir na haicearraí cuardaigh a lódáil.", + "The search shortcuts could not be saved.": "Níorbh fhéidir na haicearraí cuardaigh a shábháil.", + "Search {name} for {query}": "Cuardaigh {name} le haghaidh {query}", + "Type what to search for in {name}": "Clóscríobh cad atá le cuardach in {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/hr.js b/l10n/hr.js index 05f2759f4..7b9b29d4e 100644 --- a/l10n/hr.js +++ b/l10n/hr.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Spremi polja profila", "The profile fields could not be loaded." : "Polja profila nije bilo moguće učitati.", "The profile fields could not be saved." : "Polja profila nije bilo moguće spremiti.", + "Forget my usage" : "Zaboravi moje korištenje", + "Sorted alphabetically" : "Poredano abecedno", + "Sorted by most used" : "Poredano po najčešće korištenim", + "Sorted by last used" : "Poredano po posljednje korištenim", + "Sorted at random" : "Poredano nasumično", + "Sort tiles" : "Poredaj pločice", + "By hand" : "Ručno", + "Alphabetically" : "Abecedno", + "Most used" : "Najčešće korišteno", + "Last used" : "Posljednje korišteno", + "At random" : "Nasumično", + "Search shortcuts" : "Prečaci za pretraživanje", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Pretraživanje koje počinje prefiksom, npr. !t printer, ide izravno na tu stranicu u novoj kartici. Upisivanjem ? u okvir za pretraživanje prikazuju se prečaci.", + "Prefix" : "Prefiks", + "Address with {query}" : "Adresa s {query}", + "No search shortcuts yet." : "Još nema prečaca za pretraživanje.", + "Search shortcuts saved." : "Prečaci za pretraživanje su spremljeni.", + "Add shortcut" : "Dodaj prečac", + "Save search shortcuts" : "Spremi prečace za pretraživanje", + "The search shortcuts could not be loaded." : "Prečace za pretraživanje nije bilo moguće učitati.", + "The search shortcuts could not be saved." : "Prečace za pretraživanje nije bilo moguće spremiti.", + "Search {name} for {query}" : "Pretraži {name} za {query}", + "Type what to search for in {name}" : "Upišite što tražite u {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/hr.json b/l10n/hr.json index 8f27137f0..c5b5baf80 100644 --- a/l10n/hr.json +++ b/l10n/hr.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Spremi polja profila", "The profile fields could not be loaded.": "Polja profila nije bilo moguće učitati.", "The profile fields could not be saved.": "Polja profila nije bilo moguće spremiti.", + "Forget my usage": "Zaboravi moje korištenje", + "Sorted alphabetically": "Poredano abecedno", + "Sorted by most used": "Poredano po najčešće korištenim", + "Sorted by last used": "Poredano po posljednje korištenim", + "Sorted at random": "Poredano nasumično", + "Sort tiles": "Poredaj pločice", + "By hand": "Ručno", + "Alphabetically": "Abecedno", + "Most used": "Najčešće korišteno", + "Last used": "Posljednje korišteno", + "At random": "Nasumično", + "Search shortcuts": "Prečaci za pretraživanje", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Pretraživanje koje počinje prefiksom, npr. !t printer, ide izravno na tu stranicu u novoj kartici. Upisivanjem ? u okvir za pretraživanje prikazuju se prečaci.", + "Prefix": "Prefiks", + "Address with {query}": "Adresa s {query}", + "No search shortcuts yet.": "Još nema prečaca za pretraživanje.", + "Search shortcuts saved.": "Prečaci za pretraživanje su spremljeni.", + "Add shortcut": "Dodaj prečac", + "Save search shortcuts": "Spremi prečace za pretraživanje", + "The search shortcuts could not be loaded.": "Prečace za pretraživanje nije bilo moguće učitati.", + "The search shortcuts could not be saved.": "Prečace za pretraživanje nije bilo moguće spremiti.", + "Search {name} for {query}": "Pretraži {name} za {query}", + "Type what to search for in {name}": "Upišite što tražite u {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/hu.js b/l10n/hu.js index 0c0ad5349..c42408369 100644 --- a/l10n/hu.js +++ b/l10n/hu.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Profilmezők mentése", "The profile fields could not be loaded." : "A profilmezőket nem sikerült betölteni.", "The profile fields could not be saved." : "A profilmezőket nem sikerült menteni.", + "Forget my usage" : "Használatom elfelejtése", + "Sorted alphabetically" : "Betűrendbe rendezve", + "Sorted by most used" : "A leggyakrabban használtak szerint rendezve", + "Sorted by last used" : "A legutóbb használtak szerint rendezve", + "Sorted at random" : "Véletlenszerűen rendezve", + "Sort tiles" : "Csempék rendezése", + "By hand" : "Kézzel", + "Alphabetically" : "Betűrendben", + "Most used" : "Leggyakrabban használt", + "Last used" : "Legutóbb használt", + "At random" : "Véletlenszerűen", + "Search shortcuts" : "Keresési parancsikonok", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Az előtaggal kezdődő keresés, például !t printer, közvetlenül az adott oldalra visz egy új lapon. Ha ? jelet írsz a keresőmezőbe, megjelennek a parancsikonok.", + "Prefix" : "Előtag", + "Address with {query}" : "Cím {query} jelöléssel", + "No search shortcuts yet." : "Még nincsenek keresési parancsikonok.", + "Search shortcuts saved." : "Keresési parancsikonok mentve.", + "Add shortcut" : "Parancsikon hozzáadása", + "Save search shortcuts" : "Keresési parancsikonok mentése", + "The search shortcuts could not be loaded." : "A keresési parancsikonokat nem sikerült betölteni.", + "The search shortcuts could not be saved." : "A keresési parancsikonokat nem sikerült menteni.", + "Search {name} for {query}" : "Keresés itt: {name}, erre: {query}", + "Type what to search for in {name}" : "Írd be, mit keresel itt: {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/hu.json b/l10n/hu.json index 24ed3fda9..8f67f00c5 100644 --- a/l10n/hu.json +++ b/l10n/hu.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Profilmezők mentése", "The profile fields could not be loaded.": "A profilmezőket nem sikerült betölteni.", "The profile fields could not be saved.": "A profilmezőket nem sikerült menteni.", + "Forget my usage": "Használatom elfelejtése", + "Sorted alphabetically": "Betűrendbe rendezve", + "Sorted by most used": "A leggyakrabban használtak szerint rendezve", + "Sorted by last used": "A legutóbb használtak szerint rendezve", + "Sorted at random": "Véletlenszerűen rendezve", + "Sort tiles": "Csempék rendezése", + "By hand": "Kézzel", + "Alphabetically": "Betűrendben", + "Most used": "Leggyakrabban használt", + "Last used": "Legutóbb használt", + "At random": "Véletlenszerűen", + "Search shortcuts": "Keresési parancsikonok", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Az előtaggal kezdődő keresés, például !t printer, közvetlenül az adott oldalra visz egy új lapon. Ha ? jelet írsz a keresőmezőbe, megjelennek a parancsikonok.", + "Prefix": "Előtag", + "Address with {query}": "Cím {query} jelöléssel", + "No search shortcuts yet.": "Még nincsenek keresési parancsikonok.", + "Search shortcuts saved.": "Keresési parancsikonok mentve.", + "Add shortcut": "Parancsikon hozzáadása", + "Save search shortcuts": "Keresési parancsikonok mentése", + "The search shortcuts could not be loaded.": "A keresési parancsikonokat nem sikerült betölteni.", + "The search shortcuts could not be saved.": "A keresési parancsikonokat nem sikerült menteni.", + "Search {name} for {query}": "Keresés itt: {name}, erre: {query}", + "Type what to search for in {name}": "Írd be, mit keresel itt: {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/is.js b/l10n/is.js index 1ffb73a10..3ebc1e77f 100644 --- a/l10n/is.js +++ b/l10n/is.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Vista prófílreiti", "The profile fields could not be loaded." : "Ekki tókst að hlaða inn prófílreitunum.", "The profile fields could not be saved." : "Ekki tókst að vista prófílreitina.", + "Forget my usage" : "Gleyma notkun minni", + "Sorted alphabetically" : "Raðað í stafrófsröð", + "Sorted by most used" : "Raðað eftir mest notuðu", + "Sorted by last used" : "Raðað eftir síðast notuðu", + "Sorted at random" : "Raðað af handahófi", + "Sort tiles" : "Raða reitum", + "By hand" : "Handvirkt", + "Alphabetically" : "Í stafrófsröð", + "Most used" : "Mest notað", + "Last used" : "Síðast notað", + "At random" : "Af handahófi", + "Search shortcuts" : "Leitarflýtileiðir", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Leit sem byrjar á forskeyti, eins og !t printer, fer beint á þá síðu í nýjum flipa. Sláðu inn ? í leitarreit til að sjá flýtileiðirnar.", + "Prefix" : "Forskeyti", + "Address with {query}" : "Vefslóð með {query}", + "No search shortcuts yet." : "Engar leitarflýtileiðir enn.", + "Search shortcuts saved." : "Leitarflýtileiðir vistaðar.", + "Add shortcut" : "Bæta við flýtileið", + "Save search shortcuts" : "Vista leitarflýtileiðir", + "The search shortcuts could not be loaded." : "Ekki tókst að hlaða inn leitarflýtileiðum.", + "The search shortcuts could not be saved." : "Ekki tókst að vista leitarflýtileiðirnar.", + "Search {name} for {query}" : "Leita í {name} að {query}", + "Type what to search for in {name}" : "Sláðu inn hvað á að leita að í {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/is.json b/l10n/is.json index a8371a827..ffafa790c 100644 --- a/l10n/is.json +++ b/l10n/is.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Vista prófílreiti", "The profile fields could not be loaded.": "Ekki tókst að hlaða inn prófílreitunum.", "The profile fields could not be saved.": "Ekki tókst að vista prófílreitina.", + "Forget my usage": "Gleyma notkun minni", + "Sorted alphabetically": "Raðað í stafrófsröð", + "Sorted by most used": "Raðað eftir mest notuðu", + "Sorted by last used": "Raðað eftir síðast notuðu", + "Sorted at random": "Raðað af handahófi", + "Sort tiles": "Raða reitum", + "By hand": "Handvirkt", + "Alphabetically": "Í stafrófsröð", + "Most used": "Mest notað", + "Last used": "Síðast notað", + "At random": "Af handahófi", + "Search shortcuts": "Leitarflýtileiðir", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Leit sem byrjar á forskeyti, eins og !t printer, fer beint á þá síðu í nýjum flipa. Sláðu inn ? í leitarreit til að sjá flýtileiðirnar.", + "Prefix": "Forskeyti", + "Address with {query}": "Vefslóð með {query}", + "No search shortcuts yet.": "Engar leitarflýtileiðir enn.", + "Search shortcuts saved.": "Leitarflýtileiðir vistaðar.", + "Add shortcut": "Bæta við flýtileið", + "Save search shortcuts": "Vista leitarflýtileiðir", + "The search shortcuts could not be loaded.": "Ekki tókst að hlaða inn leitarflýtileiðum.", + "The search shortcuts could not be saved.": "Ekki tókst að vista leitarflýtileiðirnar.", + "Search {name} for {query}": "Leita í {name} að {query}", + "Type what to search for in {name}": "Sláðu inn hvað á að leita að í {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/it.js b/l10n/it.js index 7f13fd38f..52441bd4a 100644 --- a/l10n/it.js +++ b/l10n/it.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Salva campi del profilo", "The profile fields could not be loaded." : "Non è stato possibile caricare i campi del profilo.", "The profile fields could not be saved." : "Non è stato possibile salvare i campi del profilo.", + "Forget my usage" : "Dimentica il mio utilizzo", + "Sorted alphabetically" : "Ordinato alfabeticamente", + "Sorted by most used" : "Ordinato per più usati", + "Sorted by last used" : "Ordinato per ultimo utilizzo", + "Sorted at random" : "Ordinato a caso", + "Sort tiles" : "Ordina riquadri", + "By hand" : "A mano", + "Alphabetically" : "In ordine alfabetico", + "Most used" : "Più usati", + "Last used" : "Ultimo utilizzo", + "At random" : "A caso", + "Search shortcuts" : "Scorciatoie di ricerca", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Una ricerca che inizia con un prefisso, come !t printer, va direttamente a quel sito in una nuova scheda. Digitando ? in una casella di ricerca si vedono le scorciatoie.", + "Prefix" : "Prefisso", + "Address with {query}" : "Indirizzo con {query}", + "No search shortcuts yet." : "Nessuna scorciatoia di ricerca per ora.", + "Search shortcuts saved." : "Scorciatoie di ricerca salvate.", + "Add shortcut" : "Aggiungi scorciatoia", + "Save search shortcuts" : "Salva scorciatoie di ricerca", + "The search shortcuts could not be loaded." : "Non è stato possibile caricare le scorciatoie di ricerca.", + "The search shortcuts could not be saved." : "Non è stato possibile salvare le scorciatoie di ricerca.", + "Search {name} for {query}" : "Cerca {query} in {name}", + "Type what to search for in {name}" : "Scrivi cosa cercare in {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/it.json b/l10n/it.json index 37897cf79..f81c06f5e 100644 --- a/l10n/it.json +++ b/l10n/it.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Salva campi del profilo", "The profile fields could not be loaded.": "Non è stato possibile caricare i campi del profilo.", "The profile fields could not be saved.": "Non è stato possibile salvare i campi del profilo.", + "Forget my usage": "Dimentica il mio utilizzo", + "Sorted alphabetically": "Ordinato alfabeticamente", + "Sorted by most used": "Ordinato per più usati", + "Sorted by last used": "Ordinato per ultimo utilizzo", + "Sorted at random": "Ordinato a caso", + "Sort tiles": "Ordina riquadri", + "By hand": "A mano", + "Alphabetically": "In ordine alfabetico", + "Most used": "Più usati", + "Last used": "Ultimo utilizzo", + "At random": "A caso", + "Search shortcuts": "Scorciatoie di ricerca", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Una ricerca che inizia con un prefisso, come !t printer, va direttamente a quel sito in una nuova scheda. Digitando ? in una casella di ricerca si vedono le scorciatoie.", + "Prefix": "Prefisso", + "Address with {query}": "Indirizzo con {query}", + "No search shortcuts yet.": "Nessuna scorciatoia di ricerca per ora.", + "Search shortcuts saved.": "Scorciatoie di ricerca salvate.", + "Add shortcut": "Aggiungi scorciatoia", + "Save search shortcuts": "Salva scorciatoie di ricerca", + "The search shortcuts could not be loaded.": "Non è stato possibile caricare le scorciatoie di ricerca.", + "The search shortcuts could not be saved.": "Non è stato possibile salvare le scorciatoie di ricerca.", + "Search {name} for {query}": "Cerca {query} in {name}", + "Type what to search for in {name}": "Scrivi cosa cercare in {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/lb.js b/l10n/lb.js index d2ea990bf..7807df7a8 100644 --- a/l10n/lb.js +++ b/l10n/lb.js @@ -1324,6 +1324,29 @@ OC.L10N.register( "Save profile fields" : "Profilfelder späicheren", "The profile fields could not be loaded." : "D'Profilfelder konnten net geluede ginn.", "The profile fields could not be saved." : "D'Profilfelder konnten net gespäichert ginn.", + "Forget my usage" : "Meng Notzung vergiessen", + "Sorted alphabetically" : "Alphabetesch sortéiert", + "Sorted by most used" : "No der meeschter Notzung sortéiert", + "Sorted by last used" : "No der leschter Notzung sortéiert", + "Sorted at random" : "Zoufälleg sortéiert", + "Sort tiles" : "Kachelen sortéieren", + "By hand" : "Vun Hand", + "Alphabetically" : "Alphabetesch", + "Most used" : "Am meeschte benotzt", + "Last used" : "Lescht benotzt", + "At random" : "Zoufälleg", + "Search shortcuts" : "Sich-Ofkierzungen", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Eng Sich, déi mat engem Präfix ufänkt, wéi !t printer, geet direkt op dee Site an engem neien Tab. Mat ? am Sichfeld gesäis du d'Ofkierzungen.", + "Prefix" : "Präfix", + "Address with {query}" : "Adress mat {query}", + "No search shortcuts yet." : "Nach keng Sich-Ofkierzungen.", + "Search shortcuts saved." : "Sich-Ofkierzunge gespäichert.", + "Add shortcut" : "Ofkierzung derbäisetzen", + "Save search shortcuts" : "Sich-Ofkierzunge späicheren", + "The search shortcuts could not be loaded." : "D'Sich-Ofkierzunge konnten net geluede ginn.", + "The search shortcuts could not be saved." : "D'Sich-Ofkierzunge konnten net gespäichert ginn.", + "Search {name} for {query}" : "A {name} no {query} sichen", + "Type what to search for in {name}" : "Gëff an, wat an {name} gesicht gëtt", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/lb.json b/l10n/lb.json index 4f0fd6633..9a0afdb70 100644 --- a/l10n/lb.json +++ b/l10n/lb.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Profilfelder späicheren", "The profile fields could not be loaded.": "D'Profilfelder konnten net geluede ginn.", "The profile fields could not be saved.": "D'Profilfelder konnten net gespäichert ginn.", + "Forget my usage": "Meng Notzung vergiessen", + "Sorted alphabetically": "Alphabetesch sortéiert", + "Sorted by most used": "No der meeschter Notzung sortéiert", + "Sorted by last used": "No der leschter Notzung sortéiert", + "Sorted at random": "Zoufälleg sortéiert", + "Sort tiles": "Kachelen sortéieren", + "By hand": "Vun Hand", + "Alphabetically": "Alphabetesch", + "Most used": "Am meeschte benotzt", + "Last used": "Lescht benotzt", + "At random": "Zoufälleg", + "Search shortcuts": "Sich-Ofkierzungen", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Eng Sich, déi mat engem Präfix ufänkt, wéi !t printer, geet direkt op dee Site an engem neien Tab. Mat ? am Sichfeld gesäis du d'Ofkierzungen.", + "Prefix": "Präfix", + "Address with {query}": "Adress mat {query}", + "No search shortcuts yet.": "Nach keng Sich-Ofkierzungen.", + "Search shortcuts saved.": "Sich-Ofkierzunge gespäichert.", + "Add shortcut": "Ofkierzung derbäisetzen", + "Save search shortcuts": "Sich-Ofkierzunge späicheren", + "The search shortcuts could not be loaded.": "D'Sich-Ofkierzunge konnten net geluede ginn.", + "The search shortcuts could not be saved.": "D'Sich-Ofkierzunge konnten net gespäichert ginn.", + "Search {name} for {query}": "A {name} no {query} sichen", + "Type what to search for in {name}": "Gëff an, wat an {name} gesicht gëtt", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/lt.js b/l10n/lt.js index 0293743ab..0d6a5e13c 100644 --- a/l10n/lt.js +++ b/l10n/lt.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Išsaugoti profilio laukus", "The profile fields could not be loaded." : "Nepavyko įkelti profilio laukų.", "The profile fields could not be saved." : "Nepavyko išsaugoti profilio laukų.", + "Forget my usage" : "Pamiršti mano naudojimą", + "Sorted alphabetically" : "Surikiuota abėcėlės tvarka", + "Sorted by most used" : "Surikiuota pagal dažniausiai naudojamus", + "Sorted by last used" : "Surikiuota pagal paskutinį naudojimą", + "Sorted at random" : "Surikiuota atsitiktinai", + "Sort tiles" : "Plytelių rikiavimas", + "By hand" : "Rankiniu būdu", + "Alphabetically" : "Abėcėlės tvarka", + "Most used" : "Dažniausiai naudojami", + "Last used" : "Paskutiniai naudoti", + "At random" : "Atsitiktinai", + "Search shortcuts" : "Paieškos nuorodos", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Paieška, prasidedanti priešdėliu, pvz., !t printer, iškart atveria tą svetainę naujame skirtuke. Įvedę ? paieškos laukelyje pamatysite nuorodas.", + "Prefix" : "Priešdėlis", + "Address with {query}" : "Adresas su {query}", + "No search shortcuts yet." : "Paieškos nuorodų dar nėra.", + "Search shortcuts saved." : "Paieškos nuorodos išsaugotos.", + "Add shortcut" : "Pridėti nuorodą", + "Save search shortcuts" : "Išsaugoti paieškos nuorodas", + "The search shortcuts could not be loaded." : "Nepavyko įkelti paieškos nuorodų.", + "The search shortcuts could not be saved." : "Nepavyko išsaugoti paieškos nuorodų.", + "Search {name} for {query}" : "Ieškoti {name}: {query}", + "Type what to search for in {name}" : "Įveskite, ko ieškoti {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/lt.json b/l10n/lt.json index 0998da459..fad7429b7 100644 --- a/l10n/lt.json +++ b/l10n/lt.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Išsaugoti profilio laukus", "The profile fields could not be loaded.": "Nepavyko įkelti profilio laukų.", "The profile fields could not be saved.": "Nepavyko išsaugoti profilio laukų.", + "Forget my usage": "Pamiršti mano naudojimą", + "Sorted alphabetically": "Surikiuota abėcėlės tvarka", + "Sorted by most used": "Surikiuota pagal dažniausiai naudojamus", + "Sorted by last used": "Surikiuota pagal paskutinį naudojimą", + "Sorted at random": "Surikiuota atsitiktinai", + "Sort tiles": "Plytelių rikiavimas", + "By hand": "Rankiniu būdu", + "Alphabetically": "Abėcėlės tvarka", + "Most used": "Dažniausiai naudojami", + "Last used": "Paskutiniai naudoti", + "At random": "Atsitiktinai", + "Search shortcuts": "Paieškos nuorodos", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Paieška, prasidedanti priešdėliu, pvz., !t printer, iškart atveria tą svetainę naujame skirtuke. Įvedę ? paieškos laukelyje pamatysite nuorodas.", + "Prefix": "Priešdėlis", + "Address with {query}": "Adresas su {query}", + "No search shortcuts yet.": "Paieškos nuorodų dar nėra.", + "Search shortcuts saved.": "Paieškos nuorodos išsaugotos.", + "Add shortcut": "Pridėti nuorodą", + "Save search shortcuts": "Išsaugoti paieškos nuorodas", + "The search shortcuts could not be loaded.": "Nepavyko įkelti paieškos nuorodų.", + "The search shortcuts could not be saved.": "Nepavyko išsaugoti paieškos nuorodų.", + "Search {name} for {query}": "Ieškoti {name}: {query}", + "Type what to search for in {name}": "Įveskite, ko ieškoti {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/lv.js b/l10n/lv.js index e1a2bebd6..47b432e31 100644 --- a/l10n/lv.js +++ b/l10n/lv.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Saglabāt profila laukus", "The profile fields could not be loaded." : "Neizdevās ielādēt profila laukus.", "The profile fields could not be saved." : "Neizdevās saglabāt profila laukus.", + "Forget my usage" : "Aizmirst manu lietojumu", + "Sorted alphabetically" : "Kārtots alfabētiski", + "Sorted by most used" : "Kārtots pēc visbiežāk lietotajiem", + "Sorted by last used" : "Kārtots pēc pēdējās lietošanas", + "Sorted at random" : "Kārtots nejauši", + "Sort tiles" : "Flīžu kārtošana", + "By hand" : "Manuāli", + "Alphabetically" : "Alfabētiski", + "Most used" : "Visbiežāk lietotie", + "Last used" : "Pēdējie lietotie", + "At random" : "Nejauši", + "Search shortcuts" : "Meklēšanas saīsnes", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Meklēšana, kas sākas ar prefiksu, piemēram, !t printer, uzreiz atver šo vietni jaunā cilnē. Ierakstot ? meklēšanas lodziņā, redzamas saīsnes.", + "Prefix" : "Prefikss", + "Address with {query}" : "Adrese ar {query}", + "No search shortcuts yet." : "Meklēšanas saīsņu vēl nav.", + "Search shortcuts saved." : "Meklēšanas saīsnes saglabātas.", + "Add shortcut" : "Pievienot saīsni", + "Save search shortcuts" : "Saglabāt meklēšanas saīsnes", + "The search shortcuts could not be loaded." : "Neizdevās ielādēt meklēšanas saīsnes.", + "The search shortcuts could not be saved." : "Neizdevās saglabāt meklēšanas saīsnes.", + "Search {name} for {query}" : "Meklēt {name}: {query}", + "Type what to search for in {name}" : "Ierakstiet, ko meklēt {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/lv.json b/l10n/lv.json index 139c61200..969d689a8 100644 --- a/l10n/lv.json +++ b/l10n/lv.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Saglabāt profila laukus", "The profile fields could not be loaded.": "Neizdevās ielādēt profila laukus.", "The profile fields could not be saved.": "Neizdevās saglabāt profila laukus.", + "Forget my usage": "Aizmirst manu lietojumu", + "Sorted alphabetically": "Kārtots alfabētiski", + "Sorted by most used": "Kārtots pēc visbiežāk lietotajiem", + "Sorted by last used": "Kārtots pēc pēdējās lietošanas", + "Sorted at random": "Kārtots nejauši", + "Sort tiles": "Flīžu kārtošana", + "By hand": "Manuāli", + "Alphabetically": "Alfabētiski", + "Most used": "Visbiežāk lietotie", + "Last used": "Pēdējie lietotie", + "At random": "Nejauši", + "Search shortcuts": "Meklēšanas saīsnes", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Meklēšana, kas sākas ar prefiksu, piemēram, !t printer, uzreiz atver šo vietni jaunā cilnē. Ierakstot ? meklēšanas lodziņā, redzamas saīsnes.", + "Prefix": "Prefikss", + "Address with {query}": "Adrese ar {query}", + "No search shortcuts yet.": "Meklēšanas saīsņu vēl nav.", + "Search shortcuts saved.": "Meklēšanas saīsnes saglabātas.", + "Add shortcut": "Pievienot saīsni", + "Save search shortcuts": "Saglabāt meklēšanas saīsnes", + "The search shortcuts could not be loaded.": "Neizdevās ielādēt meklēšanas saīsnes.", + "The search shortcuts could not be saved.": "Neizdevās saglabāt meklēšanas saīsnes.", + "Search {name} for {query}": "Meklēt {name}: {query}", + "Type what to search for in {name}": "Ierakstiet, ko meklēt {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/mk.js b/l10n/mk.js index 824f4525e..37fba8bbd 100644 --- a/l10n/mk.js +++ b/l10n/mk.js @@ -1324,6 +1324,29 @@ OC.L10N.register( "Save profile fields" : "Зачувај полиња на профилот", "The profile fields could not be loaded." : "Полињата на профилот не можеа да се вчитаат.", "The profile fields could not be saved." : "Полињата на профилот не можеа да се зачуваат.", + "Forget my usage" : "Заборави ја мојата употреба", + "Sorted alphabetically" : "Подредено по азбучен ред", + "Sorted by most used" : "Подредено по најкористени", + "Sorted by last used" : "Подредено по последно користени", + "Sorted at random" : "Подредено по случаен избор", + "Sort tiles" : "Подредување на плочките", + "By hand" : "Рачно", + "Alphabetically" : "Азбучно", + "Most used" : "Најкористени", + "Last used" : "Последно користени", + "At random" : "По случаен избор", + "Search shortcuts" : "Кратенки за пребарување", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Пребарување што почнува со префикс, на пр. !t printer, оди директно на таа страница во ново јазиче. Со внесување ? во полето за пребарување се прикажуваат кратенките.", + "Prefix" : "Префикс", + "Address with {query}" : "Адреса со {query}", + "No search shortcuts yet." : "Сè уште нема кратенки за пребарување.", + "Search shortcuts saved." : "Кратенките за пребарување се зачувани.", + "Add shortcut" : "Додај кратенка", + "Save search shortcuts" : "Зачувај кратенки за пребарување", + "The search shortcuts could not be loaded." : "Кратенките за пребарување не можеа да се вчитаат.", + "The search shortcuts could not be saved." : "Кратенките за пребарување не можеа да се зачуваат.", + "Search {name} for {query}" : "Пребарај во {name} за {query}", + "Type what to search for in {name}" : "Внесете што да се бара во {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/mk.json b/l10n/mk.json index d1f2eb2ff..2480001ba 100644 --- a/l10n/mk.json +++ b/l10n/mk.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Зачувај полиња на профилот", "The profile fields could not be loaded.": "Полињата на профилот не можеа да се вчитаат.", "The profile fields could not be saved.": "Полињата на профилот не можеа да се зачуваат.", + "Forget my usage": "Заборави ја мојата употреба", + "Sorted alphabetically": "Подредено по азбучен ред", + "Sorted by most used": "Подредено по најкористени", + "Sorted by last used": "Подредено по последно користени", + "Sorted at random": "Подредено по случаен избор", + "Sort tiles": "Подредување на плочките", + "By hand": "Рачно", + "Alphabetically": "Азбучно", + "Most used": "Најкористени", + "Last used": "Последно користени", + "At random": "По случаен избор", + "Search shortcuts": "Кратенки за пребарување", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Пребарување што почнува со префикс, на пр. !t printer, оди директно на таа страница во ново јазиче. Со внесување ? во полето за пребарување се прикажуваат кратенките.", + "Prefix": "Префикс", + "Address with {query}": "Адреса со {query}", + "No search shortcuts yet.": "Сè уште нема кратенки за пребарување.", + "Search shortcuts saved.": "Кратенките за пребарување се зачувани.", + "Add shortcut": "Додај кратенка", + "Save search shortcuts": "Зачувај кратенки за пребарување", + "The search shortcuts could not be loaded.": "Кратенките за пребарување не можеа да се вчитаат.", + "The search shortcuts could not be saved.": "Кратенките за пребарување не можеа да се зачуваат.", + "Search {name} for {query}": "Пребарај во {name} за {query}", + "Type what to search for in {name}": "Внесете што да се бара во {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/mt.js b/l10n/mt.js index ea847d601..fa0ff2138 100644 --- a/l10n/mt.js +++ b/l10n/mt.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Issejvja l-oqsma tal-profil", "The profile fields could not be loaded." : "Il-oqsma tal-profil ma setgħux jitgħabbew.", "The profile fields could not be saved." : "Il-oqsma tal-profil ma setgħux jiġu salvati.", + "Forget my usage" : "Insa l-użu tiegħi", + "Sorted alphabetically" : "Issortjat alfabetikament", + "Sorted by most used" : "Issortjat skont l-aktar użati", + "Sorted by last used" : "Issortjat skont l-aħħar użu", + "Sorted at random" : "Issortjat b'mod każwali", + "Sort tiles" : "Issortja l-madum", + "By hand" : "Bl-idejn", + "Alphabetically" : "Alfabetikament", + "Most used" : "L-aktar użati", + "Last used" : "L-aħħar użati", + "At random" : "B'mod każwali", + "Search shortcuts" : "Shortcuts tat-tfittxija", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Tfittxija li tibda bi prefiss, bħal !t printer, tmur dritt għal dak is-sit f'tab ġdida. Ikteb ? f'kaxxa tat-tfittxija biex tara l-shortcuts.", + "Prefix" : "Prefiss", + "Address with {query}" : "Indirizz b'{query}", + "No search shortcuts yet." : "Għad m'hemmx shortcuts tat-tfittxija.", + "Search shortcuts saved." : "Il-shortcuts tat-tfittxija ġew salvati.", + "Add shortcut" : "Żid shortcut", + "Save search shortcuts" : "Issejvja l-shortcuts tat-tfittxija", + "The search shortcuts could not be loaded." : "Il-shortcuts tat-tfittxija ma setgħux jitgħabbew.", + "The search shortcuts could not be saved." : "Il-shortcuts tat-tfittxija ma setgħux jiġu salvati.", + "Search {name} for {query}" : "Fittex {name} għal {query}", + "Type what to search for in {name}" : "Ikteb x'tixtieq tfittex f'{name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/mt.json b/l10n/mt.json index 54d72b59d..b9d459e74 100644 --- a/l10n/mt.json +++ b/l10n/mt.json @@ -1302,6 +1302,29 @@ "Save profile fields": "Issejvja l-oqsma tal-profil", "The profile fields could not be loaded.": "Il-oqsma tal-profil ma setgħux jitgħabbew.", "The profile fields could not be saved.": "Il-oqsma tal-profil ma setgħux jiġu salvati.", + "Forget my usage": "Insa l-użu tiegħi", + "Sorted alphabetically": "Issortjat alfabetikament", + "Sorted by most used": "Issortjat skont l-aktar użati", + "Sorted by last used": "Issortjat skont l-aħħar użu", + "Sorted at random": "Issortjat b'mod każwali", + "Sort tiles": "Issortja l-madum", + "By hand": "Bl-idejn", + "Alphabetically": "Alfabetikament", + "Most used": "L-aktar użati", + "Last used": "L-aħħar użati", + "At random": "B'mod każwali", + "Search shortcuts": "Shortcuts tat-tfittxija", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Tfittxija li tibda bi prefiss, bħal !t printer, tmur dritt għal dak is-sit f'tab ġdida. Ikteb ? f'kaxxa tat-tfittxija biex tara l-shortcuts.", + "Prefix": "Prefiss", + "Address with {query}": "Indirizz b'{query}", + "No search shortcuts yet.": "Għad m'hemmx shortcuts tat-tfittxija.", + "Search shortcuts saved.": "Il-shortcuts tat-tfittxija ġew salvati.", + "Add shortcut": "Żid shortcut", + "Save search shortcuts": "Issejvja l-shortcuts tat-tfittxija", + "The search shortcuts could not be loaded.": "Il-shortcuts tat-tfittxija ma setgħux jitgħabbew.", + "The search shortcuts could not be saved.": "Il-shortcuts tat-tfittxija ma setgħux jiġu salvati.", + "Search {name} for {query}": "Fittex {name} għal {query}", + "Type what to search for in {name}": "Ikteb x'tixtieq tfittex f'{name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/nb.js b/l10n/nb.js index ffab2b345..6d1cd5603 100644 --- a/l10n/nb.js +++ b/l10n/nb.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Lagre profilfelt", "The profile fields could not be loaded." : "Profilfeltene kunne ikke lastes inn.", "The profile fields could not be saved." : "Profilfeltene kunne ikke lagres.", + "Forget my usage" : "Glem bruken min", + "Sorted alphabetically" : "Sortert alfabetisk", + "Sorted by most used" : "Sortert etter mest brukt", + "Sorted by last used" : "Sortert etter sist brukt", + "Sorted at random" : "Sortert tilfeldig", + "Sort tiles" : "Sorter fliser", + "By hand" : "Manuelt", + "Alphabetically" : "Alfabetisk", + "Most used" : "Mest brukt", + "Last used" : "Sist brukt", + "At random" : "Tilfeldig", + "Search shortcuts" : "Søkesnarveier", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Et søk som starter med et prefiks, for eksempel !t printer, går rett til det nettstedet i en ny fane. Skriv ? i et søkefelt for å se snarveiene.", + "Prefix" : "Prefiks", + "Address with {query}" : "Adresse med {query}", + "No search shortcuts yet." : "Ingen søkesnarveier ennå.", + "Search shortcuts saved." : "Søkesnarveiene er lagret.", + "Add shortcut" : "Legg til snarvei", + "Save search shortcuts" : "Lagre søkesnarveier", + "The search shortcuts could not be loaded." : "Søkesnarveiene kunne ikke lastes inn.", + "The search shortcuts could not be saved." : "Søkesnarveiene kunne ikke lagres.", + "Search {name} for {query}" : "Søk i {name} etter {query}", + "Type what to search for in {name}" : "Skriv hva du vil søke etter i {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/nb.json b/l10n/nb.json index 7a5acfb84..4a8c8e0ef 100644 --- a/l10n/nb.json +++ b/l10n/nb.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Lagre profilfelt", "The profile fields could not be loaded.": "Profilfeltene kunne ikke lastes inn.", "The profile fields could not be saved.": "Profilfeltene kunne ikke lagres.", + "Forget my usage": "Glem bruken min", + "Sorted alphabetically": "Sortert alfabetisk", + "Sorted by most used": "Sortert etter mest brukt", + "Sorted by last used": "Sortert etter sist brukt", + "Sorted at random": "Sortert tilfeldig", + "Sort tiles": "Sorter fliser", + "By hand": "Manuelt", + "Alphabetically": "Alfabetisk", + "Most used": "Mest brukt", + "Last used": "Sist brukt", + "At random": "Tilfeldig", + "Search shortcuts": "Søkesnarveier", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Et søk som starter med et prefiks, for eksempel !t printer, går rett til det nettstedet i en ny fane. Skriv ? i et søkefelt for å se snarveiene.", + "Prefix": "Prefiks", + "Address with {query}": "Adresse med {query}", + "No search shortcuts yet.": "Ingen søkesnarveier ennå.", + "Search shortcuts saved.": "Søkesnarveiene er lagret.", + "Add shortcut": "Legg til snarvei", + "Save search shortcuts": "Lagre søkesnarveier", + "The search shortcuts could not be loaded.": "Søkesnarveiene kunne ikke lastes inn.", + "The search shortcuts could not be saved.": "Søkesnarveiene kunne ikke lagres.", + "Search {name} for {query}": "Søk i {name} etter {query}", + "Type what to search for in {name}": "Skriv hva du vil søke etter i {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/nl.js b/l10n/nl.js index 6a06d4018..0c4a1fc50 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -1369,6 +1369,29 @@ OC.L10N.register( "Save profile fields" : "Profielvelden opslaan", "The profile fields could not be loaded." : "De profielvelden konden niet worden geladen.", "The profile fields could not be saved." : "De profielvelden konden niet worden opgeslagen.", + "Forget my usage" : "Mijn gebruik vergeten", + "Sorted alphabetically" : "Alfabetisch gesorteerd", + "Sorted by most used" : "Gesorteerd op meest gebruikt", + "Sorted by last used" : "Gesorteerd op laatst gebruikt", + "Sorted at random" : "Willekeurig gesorteerd", + "Sort tiles" : "Tegels sorteren", + "By hand" : "Handmatig", + "Alphabetically" : "Alfabetisch", + "Most used" : "Meest gebruikt", + "Last used" : "Laatst gebruikt", + "At random" : "Willekeurig", + "Search shortcuts" : "Zoeksnelkoppelingen", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Een zoekopdracht die begint met een voorvoegsel, zoals !t printer, gaat meteen naar die site in een nieuw tabblad. Typ ? in een zoekvak om de snelkoppelingen te zien.", + "Prefix" : "Voorvoegsel", + "Address with {query}" : "Adres met {query}", + "No search shortcuts yet." : "Nog geen zoeksnelkoppelingen.", + "Search shortcuts saved." : "Zoeksnelkoppelingen opgeslagen.", + "Add shortcut" : "Snelkoppeling toevoegen", + "Save search shortcuts" : "Zoeksnelkoppelingen opslaan", + "The search shortcuts could not be loaded." : "De zoeksnelkoppelingen konden niet worden geladen.", + "The search shortcuts could not be saved." : "De zoeksnelkoppelingen konden niet worden opgeslagen.", + "Search {name} for {query}" : "Zoek in {name} naar {query}", + "Type what to search for in {name}" : "Typ waarnaar je in {name} wilt zoeken", "Update template" : "Sjabloon bijwerken", "Working out what changes." : "We zoeken uit wat er verandert.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "Hiermee gaat \"{name}\" van versie {from} naar versie {to}. De naam, de groepen en of het de standaard is blijven zoals ze zijn.", @@ -1477,7 +1500,6 @@ OC.L10N.register( "OpenConnector is not installed — direct-URL mode only." : "OpenConnector is niet geïnstalleerd. Alleen een directe URL kan.", "OpenConnector source" : "OpenConnector-bron", "OpenConnector source id" : "Id van de OpenConnector-bron", - "Prefix" : "Voorvoegsel", "Same tab" : "Zelfde tabblad", "Source" : "Bron", "Source id is required" : "Het bron-id is verplicht", diff --git a/l10n/nl.json b/l10n/nl.json index cfd601353..4125bf824 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -1358,6 +1358,29 @@ "Save profile fields": "Profielvelden opslaan", "The profile fields could not be loaded.": "De profielvelden konden niet worden geladen.", "The profile fields could not be saved.": "De profielvelden konden niet worden opgeslagen.", + "Forget my usage": "Mijn gebruik vergeten", + "Sorted alphabetically": "Alfabetisch gesorteerd", + "Sorted by most used": "Gesorteerd op meest gebruikt", + "Sorted by last used": "Gesorteerd op laatst gebruikt", + "Sorted at random": "Willekeurig gesorteerd", + "Sort tiles": "Tegels sorteren", + "By hand": "Handmatig", + "Alphabetically": "Alfabetisch", + "Most used": "Meest gebruikt", + "Last used": "Laatst gebruikt", + "At random": "Willekeurig", + "Search shortcuts": "Zoeksnelkoppelingen", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Een zoekopdracht die begint met een voorvoegsel, zoals !t printer, gaat meteen naar die site in een nieuw tabblad. Typ ? in een zoekvak om de snelkoppelingen te zien.", + "Prefix": "Voorvoegsel", + "Address with {query}": "Adres met {query}", + "No search shortcuts yet.": "Nog geen zoeksnelkoppelingen.", + "Search shortcuts saved.": "Zoeksnelkoppelingen opgeslagen.", + "Add shortcut": "Snelkoppeling toevoegen", + "Save search shortcuts": "Zoeksnelkoppelingen opslaan", + "The search shortcuts could not be loaded.": "De zoeksnelkoppelingen konden niet worden geladen.", + "The search shortcuts could not be saved.": "De zoeksnelkoppelingen konden niet worden opgeslagen.", + "Search {name} for {query}": "Zoek in {name} naar {query}", + "Type what to search for in {name}": "Typ waarnaar je in {name} wilt zoeken", "Update template": "Sjabloon bijwerken", "Working out what changes.": "We zoeken uit wat er verandert.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "Hiermee gaat \"{name}\" van versie {from} naar versie {to}. De naam, de groepen en of het de standaard is blijven zoals ze zijn.", @@ -1466,7 +1489,6 @@ "OpenConnector is not installed — direct-URL mode only.": "OpenConnector is niet geïnstalleerd. Alleen een directe URL kan.", "OpenConnector source": "OpenConnector-bron", "OpenConnector source id": "Id van de OpenConnector-bron", - "Prefix": "Voorvoegsel", "Same tab": "Zelfde tabblad", "Source": "Bron", "Source id is required": "Het bron-id is verplicht", diff --git a/l10n/pl.js b/l10n/pl.js index 1bfd7579a..28705a4fe 100644 --- a/l10n/pl.js +++ b/l10n/pl.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Zapisz pola profilu", "The profile fields could not be loaded." : "Nie udało się wczytać pól profilu.", "The profile fields could not be saved." : "Nie udało się zapisać pól profilu.", + "Forget my usage" : "Zapomnij moje użycie", + "Sorted alphabetically" : "Posortowane alfabetycznie", + "Sorted by most used" : "Posortowane według najczęściej używanych", + "Sorted by last used" : "Posortowane według ostatnio używanych", + "Sorted at random" : "Posortowane losowo", + "Sort tiles" : "Sortuj kafelki", + "By hand" : "Ręcznie", + "Alphabetically" : "Alfabetycznie", + "Most used" : "Najczęściej używane", + "Last used" : "Ostatnio używane", + "At random" : "Losowo", + "Search shortcuts" : "Skróty wyszukiwania", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Wyszukiwanie zaczynające się od prefiksu, np. !t printer, przechodzi od razu do tej witryny w nowej karcie. Wpisanie ? w polu wyszukiwania pokazuje listę skrótów.", + "Prefix" : "Prefiks", + "Address with {query}" : "Adres z {query}", + "No search shortcuts yet." : "Brak skrótów wyszukiwania.", + "Search shortcuts saved." : "Skróty wyszukiwania zapisane.", + "Add shortcut" : "Dodaj skrót", + "Save search shortcuts" : "Zapisz skróty wyszukiwania", + "The search shortcuts could not be loaded." : "Nie udało się wczytać skrótów wyszukiwania.", + "The search shortcuts could not be saved." : "Nie udało się zapisać skrótów wyszukiwania.", + "Search {name} for {query}" : "Szukaj w {name}: {query}", + "Type what to search for in {name}" : "Wpisz, czego szukać w {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/pl.json b/l10n/pl.json index 55188b66b..f3494786f 100644 --- a/l10n/pl.json +++ b/l10n/pl.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Zapisz pola profilu", "The profile fields could not be loaded.": "Nie udało się wczytać pól profilu.", "The profile fields could not be saved.": "Nie udało się zapisać pól profilu.", + "Forget my usage": "Zapomnij moje użycie", + "Sorted alphabetically": "Posortowane alfabetycznie", + "Sorted by most used": "Posortowane według najczęściej używanych", + "Sorted by last used": "Posortowane według ostatnio używanych", + "Sorted at random": "Posortowane losowo", + "Sort tiles": "Sortuj kafelki", + "By hand": "Ręcznie", + "Alphabetically": "Alfabetycznie", + "Most used": "Najczęściej używane", + "Last used": "Ostatnio używane", + "At random": "Losowo", + "Search shortcuts": "Skróty wyszukiwania", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Wyszukiwanie zaczynające się od prefiksu, np. !t printer, przechodzi od razu do tej witryny w nowej karcie. Wpisanie ? w polu wyszukiwania pokazuje listę skrótów.", + "Prefix": "Prefiks", + "Address with {query}": "Adres z {query}", + "No search shortcuts yet.": "Brak skrótów wyszukiwania.", + "Search shortcuts saved.": "Skróty wyszukiwania zapisane.", + "Add shortcut": "Dodaj skrót", + "Save search shortcuts": "Zapisz skróty wyszukiwania", + "The search shortcuts could not be loaded.": "Nie udało się wczytać skrótów wyszukiwania.", + "The search shortcuts could not be saved.": "Nie udało się zapisać skrótów wyszukiwania.", + "Search {name} for {query}": "Szukaj w {name}: {query}", + "Type what to search for in {name}": "Wpisz, czego szukać w {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/pt.js b/l10n/pt.js index 5a0b819a5..a0d5c586b 100644 --- a/l10n/pt.js +++ b/l10n/pt.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Guardar campos do perfil", "The profile fields could not be loaded." : "Não foi possível carregar os campos do perfil.", "The profile fields could not be saved." : "Não foi possível guardar os campos do perfil.", + "Forget my usage" : "Esquecer a minha utilização", + "Sorted alphabetically" : "Ordenado alfabeticamente", + "Sorted by most used" : "Ordenado pelos mais usados", + "Sorted by last used" : "Ordenado pelo último uso", + "Sorted at random" : "Ordenado aleatoriamente", + "Sort tiles" : "Ordenar mosaicos", + "By hand" : "Manualmente", + "Alphabetically" : "Alfabeticamente", + "Most used" : "Mais usados", + "Last used" : "Último uso", + "At random" : "Aleatoriamente", + "Search shortcuts" : "Atalhos de pesquisa", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Uma pesquisa que começa com um prefixo, como !t printer, vai diretamente para esse site num novo separador. Escrever ? numa caixa de pesquisa mostra os atalhos.", + "Prefix" : "Prefixo", + "Address with {query}" : "Endereço com {query}", + "No search shortcuts yet." : "Ainda não há atalhos de pesquisa.", + "Search shortcuts saved." : "Atalhos de pesquisa guardados.", + "Add shortcut" : "Adicionar atalho", + "Save search shortcuts" : "Guardar atalhos de pesquisa", + "The search shortcuts could not be loaded." : "Não foi possível carregar os atalhos de pesquisa.", + "The search shortcuts could not be saved." : "Não foi possível guardar os atalhos de pesquisa.", + "Search {name} for {query}" : "Pesquisar {query} em {name}", + "Type what to search for in {name}" : "Escreva o que pesquisar em {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/pt.json b/l10n/pt.json index 84b8a459b..317361068 100644 --- a/l10n/pt.json +++ b/l10n/pt.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Guardar campos do perfil", "The profile fields could not be loaded.": "Não foi possível carregar os campos do perfil.", "The profile fields could not be saved.": "Não foi possível guardar os campos do perfil.", + "Forget my usage": "Esquecer a minha utilização", + "Sorted alphabetically": "Ordenado alfabeticamente", + "Sorted by most used": "Ordenado pelos mais usados", + "Sorted by last used": "Ordenado pelo último uso", + "Sorted at random": "Ordenado aleatoriamente", + "Sort tiles": "Ordenar mosaicos", + "By hand": "Manualmente", + "Alphabetically": "Alfabeticamente", + "Most used": "Mais usados", + "Last used": "Último uso", + "At random": "Aleatoriamente", + "Search shortcuts": "Atalhos de pesquisa", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Uma pesquisa que começa com um prefixo, como !t printer, vai diretamente para esse site num novo separador. Escrever ? numa caixa de pesquisa mostra os atalhos.", + "Prefix": "Prefixo", + "Address with {query}": "Endereço com {query}", + "No search shortcuts yet.": "Ainda não há atalhos de pesquisa.", + "Search shortcuts saved.": "Atalhos de pesquisa guardados.", + "Add shortcut": "Adicionar atalho", + "Save search shortcuts": "Guardar atalhos de pesquisa", + "The search shortcuts could not be loaded.": "Não foi possível carregar os atalhos de pesquisa.", + "The search shortcuts could not be saved.": "Não foi possível guardar os atalhos de pesquisa.", + "Search {name} for {query}": "Pesquisar {query} em {name}", + "Type what to search for in {name}": "Escreva o que pesquisar em {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/rm.js b/l10n/rm.js index 862eeacc6..5539baab7 100644 --- a/l10n/rm.js +++ b/l10n/rm.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Memorisar ils champs dal profil", "The profile fields could not be loaded." : "I n'è betg reussì da chargiar ils champs dal profil.", "The profile fields could not be saved." : "I n'è betg reussì da memorisar ils champs dal profil.", + "Forget my usage" : "Emblidar mes diever", + "Sorted alphabetically" : "Zavrà alfabeticamain", + "Sorted by most used" : "Zavrà tenor il diever il pli frequent", + "Sorted by last used" : "Zavrà tenor l'ultim diever", + "Sorted at random" : "Zavrà a chaschun", + "Sort tiles" : "Zavrar ils quadrels", + "By hand" : "A maun", + "Alphabetically" : "Alfabeticamain", + "Most used" : "Il pli savens duvrà", + "Last used" : "Duvrà l'ultim", + "At random" : "A chaschun", + "Search shortcuts" : "Scursanidas da tschertga", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Ina tschertga che cumenza cun in prefix, sco !t printer, va directamain a quella pagina en in nov tab. Cun ? en in champ da tschertga vesas las scursanidas.", + "Prefix" : "Prefix", + "Address with {query}" : "Adressa cun {query}", + "No search shortcuts yet." : "Anc naginas scursanidas da tschertga.", + "Search shortcuts saved." : "Scursanidas da tschertga memorisadas.", + "Add shortcut" : "Agiuntar ina scursanida", + "Save search shortcuts" : "Memorisar las scursanidas da tschertga", + "The search shortcuts could not be loaded." : "I n'è betg reussì da chargiar las scursanidas da tschertga.", + "The search shortcuts could not be saved." : "I n'è betg reussì da memorisar las scursanidas da tschertga.", + "Search {name} for {query}" : "Tschertgar en {name} suenter {query}", + "Type what to search for in {name}" : "Endatescha tge tschertgar en {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/rm.json b/l10n/rm.json index bd464e3da..0ef46e1d0 100644 --- a/l10n/rm.json +++ b/l10n/rm.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Memorisar ils champs dal profil", "The profile fields could not be loaded.": "I n'è betg reussì da chargiar ils champs dal profil.", "The profile fields could not be saved.": "I n'è betg reussì da memorisar ils champs dal profil.", + "Forget my usage": "Emblidar mes diever", + "Sorted alphabetically": "Zavrà alfabeticamain", + "Sorted by most used": "Zavrà tenor il diever il pli frequent", + "Sorted by last used": "Zavrà tenor l'ultim diever", + "Sorted at random": "Zavrà a chaschun", + "Sort tiles": "Zavrar ils quadrels", + "By hand": "A maun", + "Alphabetically": "Alfabeticamain", + "Most used": "Il pli savens duvrà", + "Last used": "Duvrà l'ultim", + "At random": "A chaschun", + "Search shortcuts": "Scursanidas da tschertga", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Ina tschertga che cumenza cun in prefix, sco !t printer, va directamain a quella pagina en in nov tab. Cun ? en in champ da tschertga vesas las scursanidas.", + "Prefix": "Prefix", + "Address with {query}": "Adressa cun {query}", + "No search shortcuts yet.": "Anc naginas scursanidas da tschertga.", + "Search shortcuts saved.": "Scursanidas da tschertga memorisadas.", + "Add shortcut": "Agiuntar ina scursanida", + "Save search shortcuts": "Memorisar las scursanidas da tschertga", + "The search shortcuts could not be loaded.": "I n'è betg reussì da chargiar las scursanidas da tschertga.", + "The search shortcuts could not be saved.": "I n'è betg reussì da memorisar las scursanidas da tschertga.", + "Search {name} for {query}": "Tschertgar en {name} suenter {query}", + "Type what to search for in {name}": "Endatescha tge tschertgar en {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ro.js b/l10n/ro.js index bd68397ef..88a874e6c 100644 --- a/l10n/ro.js +++ b/l10n/ro.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Salvează câmpurile de profil", "The profile fields could not be loaded." : "Câmpurile de profil nu au putut fi încărcate.", "The profile fields could not be saved." : "Câmpurile de profil nu au putut fi salvate.", + "Forget my usage" : "Uită utilizarea mea", + "Sorted alphabetically" : "Sortat alfabetic", + "Sorted by most used" : "Sortat după cele mai folosite", + "Sorted by last used" : "Sortat după ultima utilizare", + "Sorted at random" : "Sortat aleatoriu", + "Sort tiles" : "Sortează dalele", + "By hand" : "Manual", + "Alphabetically" : "Alfabetic", + "Most used" : "Cele mai folosite", + "Last used" : "Ultima utilizare", + "At random" : "Aleatoriu", + "Search shortcuts" : "Scurtături de căutare", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "O căutare care începe cu un prefix, de exemplu !t printer, merge direct pe acel site într-o filă nouă. Tastând ? într-o casetă de căutare vezi scurtăturile.", + "Prefix" : "Prefix", + "Address with {query}" : "Adresă cu {query}", + "No search shortcuts yet." : "Încă nu există scurtături de căutare.", + "Search shortcuts saved." : "Scurtăturile de căutare au fost salvate.", + "Add shortcut" : "Adaugă scurtătură", + "Save search shortcuts" : "Salvează scurtăturile de căutare", + "The search shortcuts could not be loaded." : "Scurtăturile de căutare nu au putut fi încărcate.", + "The search shortcuts could not be saved." : "Scurtăturile de căutare nu au putut fi salvate.", + "Search {name} for {query}" : "Caută în {name} după {query}", + "Type what to search for in {name}" : "Scrie ce să cauți în {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ro.json b/l10n/ro.json index b4ff00a56..884ad3676 100644 --- a/l10n/ro.json +++ b/l10n/ro.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Salvează câmpurile de profil", "The profile fields could not be loaded.": "Câmpurile de profil nu au putut fi încărcate.", "The profile fields could not be saved.": "Câmpurile de profil nu au putut fi salvate.", + "Forget my usage": "Uită utilizarea mea", + "Sorted alphabetically": "Sortat alfabetic", + "Sorted by most used": "Sortat după cele mai folosite", + "Sorted by last used": "Sortat după ultima utilizare", + "Sorted at random": "Sortat aleatoriu", + "Sort tiles": "Sortează dalele", + "By hand": "Manual", + "Alphabetically": "Alfabetic", + "Most used": "Cele mai folosite", + "Last used": "Ultima utilizare", + "At random": "Aleatoriu", + "Search shortcuts": "Scurtături de căutare", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "O căutare care începe cu un prefix, de exemplu !t printer, merge direct pe acel site într-o filă nouă. Tastând ? într-o casetă de căutare vezi scurtăturile.", + "Prefix": "Prefix", + "Address with {query}": "Adresă cu {query}", + "No search shortcuts yet.": "Încă nu există scurtături de căutare.", + "Search shortcuts saved.": "Scurtăturile de căutare au fost salvate.", + "Add shortcut": "Adaugă scurtătură", + "Save search shortcuts": "Salvează scurtăturile de căutare", + "The search shortcuts could not be loaded.": "Scurtăturile de căutare nu au putut fi încărcate.", + "The search shortcuts could not be saved.": "Scurtăturile de căutare nu au putut fi salvate.", + "Search {name} for {query}": "Caută în {name} după {query}", + "Type what to search for in {name}": "Scrie ce să cauți în {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ru.js b/l10n/ru.js index daa276ee2..2de936003 100644 --- a/l10n/ru.js +++ b/l10n/ru.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Сохранить поля профиля", "The profile fields could not be loaded." : "Не удалось загрузить поля профиля.", "The profile fields could not be saved." : "Не удалось сохранить поля профиля.", + "Forget my usage" : "Забыть моё использование", + "Sorted alphabetically" : "Отсортировано по алфавиту", + "Sorted by most used" : "Отсортировано по частоте использования", + "Sorted by last used" : "Отсортировано по последнему использованию", + "Sorted at random" : "Отсортировано случайно", + "Sort tiles" : "Сортировка плиток", + "By hand" : "Вручную", + "Alphabetically" : "По алфавиту", + "Most used" : "Самые используемые", + "Last used" : "Последние использованные", + "At random" : "Случайно", + "Search shortcuts" : "Ярлыки поиска", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Поиск, начинающийся с префикса, например !t printer, сразу открывает этот сайт в новой вкладке. Введите ? в поле поиска, чтобы увидеть ярлыки.", + "Prefix" : "Префикс", + "Address with {query}" : "Адрес с {query}", + "No search shortcuts yet." : "Ярлыков поиска пока нет.", + "Search shortcuts saved." : "Ярлыки поиска сохранены.", + "Add shortcut" : "Добавить ярлык", + "Save search shortcuts" : "Сохранить ярлыки поиска", + "The search shortcuts could not be loaded." : "Не удалось загрузить ярлыки поиска.", + "The search shortcuts could not be saved." : "Не удалось сохранить ярлыки поиска.", + "Search {name} for {query}" : "Искать в {name}: {query}", + "Type what to search for in {name}" : "Введите, что искать в {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/ru.json b/l10n/ru.json index f8a601b56..4796e48ab 100644 --- a/l10n/ru.json +++ b/l10n/ru.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Сохранить поля профиля", "The profile fields could not be loaded.": "Не удалось загрузить поля профиля.", "The profile fields could not be saved.": "Не удалось сохранить поля профиля.", + "Forget my usage": "Забыть моё использование", + "Sorted alphabetically": "Отсортировано по алфавиту", + "Sorted by most used": "Отсортировано по частоте использования", + "Sorted by last used": "Отсортировано по последнему использованию", + "Sorted at random": "Отсортировано случайно", + "Sort tiles": "Сортировка плиток", + "By hand": "Вручную", + "Alphabetically": "По алфавиту", + "Most used": "Самые используемые", + "Last used": "Последние использованные", + "At random": "Случайно", + "Search shortcuts": "Ярлыки поиска", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Поиск, начинающийся с префикса, например !t printer, сразу открывает этот сайт в новой вкладке. Введите ? в поле поиска, чтобы увидеть ярлыки.", + "Prefix": "Префикс", + "Address with {query}": "Адрес с {query}", + "No search shortcuts yet.": "Ярлыков поиска пока нет.", + "Search shortcuts saved.": "Ярлыки поиска сохранены.", + "Add shortcut": "Добавить ярлык", + "Save search shortcuts": "Сохранить ярлыки поиска", + "The search shortcuts could not be loaded.": "Не удалось загрузить ярлыки поиска.", + "The search shortcuts could not be saved.": "Не удалось сохранить ярлыки поиска.", + "Search {name} for {query}": "Искать в {name}: {query}", + "Type what to search for in {name}": "Введите, что искать в {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sk.js b/l10n/sk.js index b471b97c4..88633c975 100644 --- a/l10n/sk.js +++ b/l10n/sk.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Uložiť polia profilu", "The profile fields could not be loaded." : "Polia profilu sa nepodarilo načítať.", "The profile fields could not be saved." : "Polia profilu sa nepodarilo uložiť.", + "Forget my usage" : "Zabudnúť moje používanie", + "Sorted alphabetically" : "Zoradené abecedne", + "Sorted by most used" : "Zoradené podľa najpoužívanejších", + "Sorted by last used" : "Zoradené podľa naposledy použitých", + "Sorted at random" : "Zoradené náhodne", + "Sort tiles" : "Zoradenie dlaždíc", + "By hand" : "Ručne", + "Alphabetically" : "Abecedne", + "Most used" : "Najpoužívanejšie", + "Last used" : "Naposledy použité", + "At random" : "Náhodne", + "Search shortcuts" : "Skratky vyhľadávania", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Vyhľadávanie, ktoré začína predponou, napríklad !t printer, prejde priamo na danú stránku v novej karte. Napísaním ? do vyhľadávacieho poľa zobrazíte skratky.", + "Prefix" : "Predpona", + "Address with {query}" : "Adresa s {query}", + "No search shortcuts yet." : "Zatiaľ žiadne skratky vyhľadávania.", + "Search shortcuts saved." : "Skratky vyhľadávania sú uložené.", + "Add shortcut" : "Pridať skratku", + "Save search shortcuts" : "Uložiť skratky vyhľadávania", + "The search shortcuts could not be loaded." : "Skratky vyhľadávania sa nepodarilo načítať.", + "The search shortcuts could not be saved." : "Skratky vyhľadávania sa nepodarilo uložiť.", + "Search {name} for {query}" : "Hľadať v {name}: {query}", + "Type what to search for in {name}" : "Napíšte, čo hľadať v {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sk.json b/l10n/sk.json index 1d33aa11f..885db134b 100644 --- a/l10n/sk.json +++ b/l10n/sk.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Uložiť polia profilu", "The profile fields could not be loaded.": "Polia profilu sa nepodarilo načítať.", "The profile fields could not be saved.": "Polia profilu sa nepodarilo uložiť.", + "Forget my usage": "Zabudnúť moje používanie", + "Sorted alphabetically": "Zoradené abecedne", + "Sorted by most used": "Zoradené podľa najpoužívanejších", + "Sorted by last used": "Zoradené podľa naposledy použitých", + "Sorted at random": "Zoradené náhodne", + "Sort tiles": "Zoradenie dlaždíc", + "By hand": "Ručne", + "Alphabetically": "Abecedne", + "Most used": "Najpoužívanejšie", + "Last used": "Naposledy použité", + "At random": "Náhodne", + "Search shortcuts": "Skratky vyhľadávania", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Vyhľadávanie, ktoré začína predponou, napríklad !t printer, prejde priamo na danú stránku v novej karte. Napísaním ? do vyhľadávacieho poľa zobrazíte skratky.", + "Prefix": "Predpona", + "Address with {query}": "Adresa s {query}", + "No search shortcuts yet.": "Zatiaľ žiadne skratky vyhľadávania.", + "Search shortcuts saved.": "Skratky vyhľadávania sú uložené.", + "Add shortcut": "Pridať skratku", + "Save search shortcuts": "Uložiť skratky vyhľadávania", + "The search shortcuts could not be loaded.": "Skratky vyhľadávania sa nepodarilo načítať.", + "The search shortcuts could not be saved.": "Skratky vyhľadávania sa nepodarilo uložiť.", + "Search {name} for {query}": "Hľadať v {name}: {query}", + "Type what to search for in {name}": "Napíšte, čo hľadať v {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sl.js b/l10n/sl.js index 1e5835e03..01d223cf3 100644 --- a/l10n/sl.js +++ b/l10n/sl.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Shrani polja profila", "The profile fields could not be loaded." : "Polj profila ni bilo mogoče naložiti.", "The profile fields could not be saved." : "Polj profila ni bilo mogoče shraniti.", + "Forget my usage" : "Pozabi mojo uporabo", + "Sorted alphabetically" : "Razvrščeno po abecedi", + "Sorted by most used" : "Razvrščeno po najpogosteje uporabljenih", + "Sorted by last used" : "Razvrščeno po zadnji uporabi", + "Sorted at random" : "Razvrščeno naključno", + "Sort tiles" : "Razvrsti ploščice", + "By hand" : "Ročno", + "Alphabetically" : "Po abecedi", + "Most used" : "Najpogosteje uporabljeno", + "Last used" : "Nazadnje uporabljeno", + "At random" : "Naključno", + "Search shortcuts" : "Bližnjice iskanja", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Iskanje, ki se začne s predpono, na primer !t printer, gre neposredno na to spletno mesto v novem zavihku. Če v iskalno polje vnesete ?, se prikažejo bližnjice.", + "Prefix" : "Predpona", + "Address with {query}" : "Naslov z {query}", + "No search shortcuts yet." : "Bližnjic iskanja še ni.", + "Search shortcuts saved." : "Bližnjice iskanja so shranjene.", + "Add shortcut" : "Dodaj bližnjico", + "Save search shortcuts" : "Shrani bližnjice iskanja", + "The search shortcuts could not be loaded." : "Bližnjic iskanja ni bilo mogoče naložiti.", + "The search shortcuts could not be saved." : "Bližnjic iskanja ni bilo mogoče shraniti.", + "Search {name} for {query}" : "Išči v {name}: {query}", + "Type what to search for in {name}" : "Vnesite, kaj iskati v {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sl.json b/l10n/sl.json index c3fffe8a5..063255893 100644 --- a/l10n/sl.json +++ b/l10n/sl.json @@ -1302,6 +1302,29 @@ "Save profile fields": "Shrani polja profila", "The profile fields could not be loaded.": "Polj profila ni bilo mogoče naložiti.", "The profile fields could not be saved.": "Polj profila ni bilo mogoče shraniti.", + "Forget my usage": "Pozabi mojo uporabo", + "Sorted alphabetically": "Razvrščeno po abecedi", + "Sorted by most used": "Razvrščeno po najpogosteje uporabljenih", + "Sorted by last used": "Razvrščeno po zadnji uporabi", + "Sorted at random": "Razvrščeno naključno", + "Sort tiles": "Razvrsti ploščice", + "By hand": "Ročno", + "Alphabetically": "Po abecedi", + "Most used": "Najpogosteje uporabljeno", + "Last used": "Nazadnje uporabljeno", + "At random": "Naključno", + "Search shortcuts": "Bližnjice iskanja", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Iskanje, ki se začne s predpono, na primer !t printer, gre neposredno na to spletno mesto v novem zavihku. Če v iskalno polje vnesete ?, se prikažejo bližnjice.", + "Prefix": "Predpona", + "Address with {query}": "Naslov z {query}", + "No search shortcuts yet.": "Bližnjic iskanja še ni.", + "Search shortcuts saved.": "Bližnjice iskanja so shranjene.", + "Add shortcut": "Dodaj bližnjico", + "Save search shortcuts": "Shrani bližnjice iskanja", + "The search shortcuts could not be loaded.": "Bližnjic iskanja ni bilo mogoče naložiti.", + "The search shortcuts could not be saved.": "Bližnjic iskanja ni bilo mogoče shraniti.", + "Search {name} for {query}": "Išči v {name}: {query}", + "Type what to search for in {name}": "Vnesite, kaj iskati v {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sq.js b/l10n/sq.js index 7c6d416f4..e8efd2bb6 100644 --- a/l10n/sq.js +++ b/l10n/sq.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Ruaj fushat e profilit", "The profile fields could not be loaded." : "Fushat e profilit nuk u ngarkuan.", "The profile fields could not be saved." : "Fushat e profilit nuk u ruajtën.", + "Forget my usage" : "Harro përdorimin tim", + "Sorted alphabetically" : "Renditur sipas alfabetit", + "Sorted by most used" : "Renditur sipas më të përdorurave", + "Sorted by last used" : "Renditur sipas përdorimit të fundit", + "Sorted at random" : "Renditur rastësisht", + "Sort tiles" : "Rendit pllakat", + "By hand" : "Me dorë", + "Alphabetically" : "Sipas alfabetit", + "Most used" : "Më të përdorurat", + "Last used" : "Përdorur së fundi", + "At random" : "Rastësisht", + "Search shortcuts" : "Shkurtore kërkimi", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Një kërkim që nis me një parashtesë, si !t printer, shkon drejt e në atë faqe në një skedë të re. Duke shkruar ? në kutinë e kërkimit shfaqen shkurtoret.", + "Prefix" : "Parashtesë", + "Address with {query}" : "Adresë me {query}", + "No search shortcuts yet." : "Ende nuk ka shkurtore kërkimi.", + "Search shortcuts saved." : "Shkurtoret e kërkimit u ruajtën.", + "Add shortcut" : "Shto shkurtore", + "Save search shortcuts" : "Ruaj shkurtoret e kërkimit", + "The search shortcuts could not be loaded." : "Shkurtoret e kërkimit nuk u ngarkuan.", + "The search shortcuts could not be saved." : "Shkurtoret e kërkimit nuk u ruajtën.", + "Search {name} for {query}" : "Kërko në {name} për {query}", + "Type what to search for in {name}" : "Shkruaj çfarë të kërkosh në {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sq.json b/l10n/sq.json index 14a4bcc28..37d5da818 100644 --- a/l10n/sq.json +++ b/l10n/sq.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Ruaj fushat e profilit", "The profile fields could not be loaded.": "Fushat e profilit nuk u ngarkuan.", "The profile fields could not be saved.": "Fushat e profilit nuk u ruajtën.", + "Forget my usage": "Harro përdorimin tim", + "Sorted alphabetically": "Renditur sipas alfabetit", + "Sorted by most used": "Renditur sipas më të përdorurave", + "Sorted by last used": "Renditur sipas përdorimit të fundit", + "Sorted at random": "Renditur rastësisht", + "Sort tiles": "Rendit pllakat", + "By hand": "Me dorë", + "Alphabetically": "Sipas alfabetit", + "Most used": "Më të përdorurat", + "Last used": "Përdorur së fundi", + "At random": "Rastësisht", + "Search shortcuts": "Shkurtore kërkimi", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Një kërkim që nis me një parashtesë, si !t printer, shkon drejt e në atë faqe në një skedë të re. Duke shkruar ? në kutinë e kërkimit shfaqen shkurtoret.", + "Prefix": "Parashtesë", + "Address with {query}": "Adresë me {query}", + "No search shortcuts yet.": "Ende nuk ka shkurtore kërkimi.", + "Search shortcuts saved.": "Shkurtoret e kërkimit u ruajtën.", + "Add shortcut": "Shto shkurtore", + "Save search shortcuts": "Ruaj shkurtoret e kërkimit", + "The search shortcuts could not be loaded.": "Shkurtoret e kërkimit nuk u ngarkuan.", + "The search shortcuts could not be saved.": "Shkurtoret e kërkimit nuk u ruajtën.", + "Search {name} for {query}": "Kërko në {name} për {query}", + "Type what to search for in {name}": "Shkruaj çfarë të kërkosh në {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sr.js b/l10n/sr.js index e712229a1..a04af8b51 100644 --- a/l10n/sr.js +++ b/l10n/sr.js @@ -1324,6 +1324,29 @@ OC.L10N.register( "Save profile fields" : "Сачувај поља профила", "The profile fields could not be loaded." : "Поља профила нису могла да се учитају.", "The profile fields could not be saved." : "Поља профила нису могла да се сачувају.", + "Forget my usage" : "Заборави моје коришћење", + "Sorted alphabetically" : "Поређано абецедно", + "Sorted by most used" : "Поређано по најкоришћенијим", + "Sorted by last used" : "Поређано по последње коришћеним", + "Sorted at random" : "Поређано насумично", + "Sort tiles" : "Поређај плочице", + "By hand" : "Ручно", + "Alphabetically" : "Абецедно", + "Most used" : "Најкоришћеније", + "Last used" : "Последње коришћено", + "At random" : "Насумично", + "Search shortcuts" : "Пречице за претрагу", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Претрага која почиње префиксом, нпр. !t printer, иде директно на ту страницу у новој картици. Уносом ? у поље за претрагу приказују се пречице.", + "Prefix" : "Префикс", + "Address with {query}" : "Адреса са {query}", + "No search shortcuts yet." : "Још нема пречица за претрагу.", + "Search shortcuts saved." : "Пречице за претрагу су сачуване.", + "Add shortcut" : "Додај пречицу", + "Save search shortcuts" : "Сачувај пречице за претрагу", + "The search shortcuts could not be loaded." : "Пречице за претрагу нису могле да се учитају.", + "The search shortcuts could not be saved." : "Пречице за претрагу нису могле да се сачувају.", + "Search {name} for {query}" : "Претражи {name} за {query}", + "Type what to search for in {name}" : "Унесите шта тражите у {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sr.json b/l10n/sr.json index 755f9acd8..11ed13b7a 100644 --- a/l10n/sr.json +++ b/l10n/sr.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Сачувај поља профила", "The profile fields could not be loaded.": "Поља профила нису могла да се учитају.", "The profile fields could not be saved.": "Поља профила нису могла да се сачувају.", + "Forget my usage": "Заборави моје коришћење", + "Sorted alphabetically": "Поређано абецедно", + "Sorted by most used": "Поређано по најкоришћенијим", + "Sorted by last used": "Поређано по последње коришћеним", + "Sorted at random": "Поређано насумично", + "Sort tiles": "Поређај плочице", + "By hand": "Ручно", + "Alphabetically": "Абецедно", + "Most used": "Најкоришћеније", + "Last used": "Последње коришћено", + "At random": "Насумично", + "Search shortcuts": "Пречице за претрагу", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Претрага која почиње префиксом, нпр. !t printer, иде директно на ту страницу у новој картици. Уносом ? у поље за претрагу приказују се пречице.", + "Prefix": "Префикс", + "Address with {query}": "Адреса са {query}", + "No search shortcuts yet.": "Још нема пречица за претрагу.", + "Search shortcuts saved.": "Пречице за претрагу су сачуване.", + "Add shortcut": "Додај пречицу", + "Save search shortcuts": "Сачувај пречице за претрагу", + "The search shortcuts could not be loaded.": "Пречице за претрагу нису могле да се учитају.", + "The search shortcuts could not be saved.": "Пречице за претрагу нису могле да се сачувају.", + "Search {name} for {query}": "Претражи {name} за {query}", + "Type what to search for in {name}": "Унесите шта тражите у {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sv.js b/l10n/sv.js index 5493c64fd..378e0d328 100644 --- a/l10n/sv.js +++ b/l10n/sv.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Spara profilfält", "The profile fields could not be loaded." : "Profilfälten kunde inte läsas in.", "The profile fields could not be saved." : "Profilfälten kunde inte sparas.", + "Forget my usage" : "Glöm min användning", + "Sorted alphabetically" : "Sorterat i bokstavsordning", + "Sorted by most used" : "Sorterat efter mest använda", + "Sorted by last used" : "Sorterat efter senast använda", + "Sorted at random" : "Sorterat slumpmässigt", + "Sort tiles" : "Sortera paneler", + "By hand" : "Manuellt", + "Alphabetically" : "I bokstavsordning", + "Most used" : "Mest använda", + "Last used" : "Senast använda", + "At random" : "Slumpmässigt", + "Search shortcuts" : "Sökgenvägar", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "En sökning som börjar med ett prefix, till exempel !t printer, går direkt till den webbplatsen i en ny flik. Skriv ? i en sökruta för att se genvägarna.", + "Prefix" : "Prefix", + "Address with {query}" : "Adress med {query}", + "No search shortcuts yet." : "Inga sökgenvägar ännu.", + "Search shortcuts saved." : "Sökgenvägarna har sparats.", + "Add shortcut" : "Lägg till genväg", + "Save search shortcuts" : "Spara sökgenvägar", + "The search shortcuts could not be loaded." : "Sökgenvägarna kunde inte läsas in.", + "The search shortcuts could not be saved." : "Sökgenvägarna kunde inte sparas.", + "Search {name} for {query}" : "Sök i {name} efter {query}", + "Type what to search for in {name}" : "Skriv vad du vill söka efter i {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/sv.json b/l10n/sv.json index 75370ec03..cc8a2d450 100644 --- a/l10n/sv.json +++ b/l10n/sv.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Spara profilfält", "The profile fields could not be loaded.": "Profilfälten kunde inte läsas in.", "The profile fields could not be saved.": "Profilfälten kunde inte sparas.", + "Forget my usage": "Glöm min användning", + "Sorted alphabetically": "Sorterat i bokstavsordning", + "Sorted by most used": "Sorterat efter mest använda", + "Sorted by last used": "Sorterat efter senast använda", + "Sorted at random": "Sorterat slumpmässigt", + "Sort tiles": "Sortera paneler", + "By hand": "Manuellt", + "Alphabetically": "I bokstavsordning", + "Most used": "Mest använda", + "Last used": "Senast använda", + "At random": "Slumpmässigt", + "Search shortcuts": "Sökgenvägar", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "En sökning som börjar med ett prefix, till exempel !t printer, går direkt till den webbplatsen i en ny flik. Skriv ? i en sökruta för att se genvägarna.", + "Prefix": "Prefix", + "Address with {query}": "Adress med {query}", + "No search shortcuts yet.": "Inga sökgenvägar ännu.", + "Search shortcuts saved.": "Sökgenvägarna har sparats.", + "Add shortcut": "Lägg till genväg", + "Save search shortcuts": "Spara sökgenvägar", + "The search shortcuts could not be loaded.": "Sökgenvägarna kunde inte läsas in.", + "The search shortcuts could not be saved.": "Sökgenvägarna kunde inte sparas.", + "Search {name} for {query}": "Sök i {name} efter {query}", + "Type what to search for in {name}": "Skriv vad du vill söka efter i {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/tr.js b/l10n/tr.js index 12b52c549..3256a49d6 100644 --- a/l10n/tr.js +++ b/l10n/tr.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Profil alanlarını kaydet", "The profile fields could not be loaded." : "Profil alanları yüklenemedi.", "The profile fields could not be saved." : "Profil alanları kaydedilemedi.", + "Forget my usage" : "Kullanımımı unut", + "Sorted alphabetically" : "Alfabetik sıralandı", + "Sorted by most used" : "En çok kullanılana göre sıralandı", + "Sorted by last used" : "Son kullanılana göre sıralandı", + "Sorted at random" : "Rastgele sıralandı", + "Sort tiles" : "Kutucukları sırala", + "By hand" : "Elle", + "Alphabetically" : "Alfabetik", + "Most used" : "En çok kullanılan", + "Last used" : "Son kullanılan", + "At random" : "Rastgele", + "Search shortcuts" : "Arama kısayolları", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "!t printer gibi bir önekle başlayan arama, doğrudan o siteye yeni bir sekmede gider. Arama kutusuna ? yazınca kısayollar listelenir.", + "Prefix" : "Önek", + "Address with {query}" : "{query} içeren adres", + "No search shortcuts yet." : "Henüz arama kısayolu yok.", + "Search shortcuts saved." : "Arama kısayolları kaydedildi.", + "Add shortcut" : "Kısayol ekle", + "Save search shortcuts" : "Arama kısayollarını kaydet", + "The search shortcuts could not be loaded." : "Arama kısayolları yüklenemedi.", + "The search shortcuts could not be saved." : "Arama kısayolları kaydedilemedi.", + "Search {name} for {query}" : "{name} içinde {query} ara", + "Type what to search for in {name}" : "{name} içinde ne aranacağını yaz", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/tr.json b/l10n/tr.json index 9c9c7764c..bed0bf709 100644 --- a/l10n/tr.json +++ b/l10n/tr.json @@ -1300,6 +1300,29 @@ "Save profile fields": "Profil alanlarını kaydet", "The profile fields could not be loaded.": "Profil alanları yüklenemedi.", "The profile fields could not be saved.": "Profil alanları kaydedilemedi.", + "Forget my usage": "Kullanımımı unut", + "Sorted alphabetically": "Alfabetik sıralandı", + "Sorted by most used": "En çok kullanılana göre sıralandı", + "Sorted by last used": "Son kullanılana göre sıralandı", + "Sorted at random": "Rastgele sıralandı", + "Sort tiles": "Kutucukları sırala", + "By hand": "Elle", + "Alphabetically": "Alfabetik", + "Most used": "En çok kullanılan", + "Last used": "Son kullanılan", + "At random": "Rastgele", + "Search shortcuts": "Arama kısayolları", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "!t printer gibi bir önekle başlayan arama, doğrudan o siteye yeni bir sekmede gider. Arama kutusuna ? yazınca kısayollar listelenir.", + "Prefix": "Önek", + "Address with {query}": "{query} içeren adres", + "No search shortcuts yet.": "Henüz arama kısayolu yok.", + "Search shortcuts saved.": "Arama kısayolları kaydedildi.", + "Add shortcut": "Kısayol ekle", + "Save search shortcuts": "Arama kısayollarını kaydet", + "The search shortcuts could not be loaded.": "Arama kısayolları yüklenemedi.", + "The search shortcuts could not be saved.": "Arama kısayolları kaydedilemedi.", + "Search {name} for {query}": "{name} içinde {query} ara", + "Type what to search for in {name}": "{name} içinde ne aranacağını yaz", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/uk.js b/l10n/uk.js index 74fe4050f..b33b5e47f 100644 --- a/l10n/uk.js +++ b/l10n/uk.js @@ -1333,6 +1333,29 @@ OC.L10N.register( "Save profile fields" : "Зберегти поля профілю", "The profile fields could not be loaded." : "Не вдалося завантажити поля профілю.", "The profile fields could not be saved." : "Не вдалося зберегти поля профілю.", + "Forget my usage" : "Забути моє використання", + "Sorted alphabetically" : "Відсортовано за абеткою", + "Sorted by most used" : "Відсортовано за частотою використання", + "Sorted by last used" : "Відсортовано за останнім використанням", + "Sorted at random" : "Відсортовано випадково", + "Sort tiles" : "Сортування плиток", + "By hand" : "Вручну", + "Alphabetically" : "За абеткою", + "Most used" : "Найчастіше використовувані", + "Last used" : "Останні використані", + "At random" : "Випадково", + "Search shortcuts" : "Ярлики пошуку", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts." : "Пошук, що починається з префікса, наприклад !t printer, одразу відкриває цей сайт у новій вкладці. Введіть ? у полі пошуку, щоб побачити ярлики.", + "Prefix" : "Префікс", + "Address with {query}" : "Адреса з {query}", + "No search shortcuts yet." : "Ярликів пошуку ще немає.", + "Search shortcuts saved." : "Ярлики пошуку збережено.", + "Add shortcut" : "Додати ярлик", + "Save search shortcuts" : "Зберегти ярлики пошуку", + "The search shortcuts could not be loaded." : "Не вдалося завантажити ярлики пошуку.", + "The search shortcuts could not be saved." : "Не вдалося зберегти ярлики пошуку.", + "Search {name} for {query}" : "Шукати в {name}: {query}", + "Type what to search for in {name}" : "Введіть, що шукати в {name}", "Update template" : "Update template", "Working out what changes." : "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are." : "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/l10n/uk.json b/l10n/uk.json index 65ef9f8ed..f1847f301 100644 --- a/l10n/uk.json +++ b/l10n/uk.json @@ -1301,6 +1301,29 @@ "Save profile fields": "Зберегти поля профілю", "The profile fields could not be loaded.": "Не вдалося завантажити поля профілю.", "The profile fields could not be saved.": "Не вдалося зберегти поля профілю.", + "Forget my usage": "Забути моє використання", + "Sorted alphabetically": "Відсортовано за абеткою", + "Sorted by most used": "Відсортовано за частотою використання", + "Sorted by last used": "Відсортовано за останнім використанням", + "Sorted at random": "Відсортовано випадково", + "Sort tiles": "Сортування плиток", + "By hand": "Вручну", + "Alphabetically": "За абеткою", + "Most used": "Найчастіше використовувані", + "Last used": "Останні використані", + "At random": "Випадково", + "Search shortcuts": "Ярлики пошуку", + "A search that starts with a prefix, such as !t printer, goes straight to that site in a new tab. Typing ? in a search box lists the shortcuts.": "Пошук, що починається з префікса, наприклад !t printer, одразу відкриває цей сайт у новій вкладці. Введіть ? у полі пошуку, щоб побачити ярлики.", + "Prefix": "Префікс", + "Address with {query}": "Адреса з {query}", + "No search shortcuts yet.": "Ярликів пошуку ще немає.", + "Search shortcuts saved.": "Ярлики пошуку збережено.", + "Add shortcut": "Додати ярлик", + "Save search shortcuts": "Зберегти ярлики пошуку", + "The search shortcuts could not be loaded.": "Не вдалося завантажити ярлики пошуку.", + "The search shortcuts could not be saved.": "Не вдалося зберегти ярлики пошуку.", + "Search {name} for {query}": "Шукати в {name}: {query}", + "Type what to search for in {name}": "Введіть, що шукати в {name}", "Update template": "Update template", "Working out what changes.": "Working out what changes.", "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.": "This brings \"{name}\" from version {from} to version {to}. Its name, its groups and whether it is the default stay as they are.", diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index c74852dfd..8d12d0801 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -34,6 +34,7 @@ use OCA\LaunchPad\Service\InitialState\Page; use OCA\LaunchPad\Service\InitialStateBuilder; use OCA\LaunchPad\Service\RoleFeaturePermissionService; +use OCA\LaunchPad\Service\SearchShortcutService; use OCA\LaunchPad\Service\WidgetService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Attribute\AnonRateLimit; @@ -91,6 +92,9 @@ class PageController extends Controller { * admin setting * (tile-quick-search * REQ-QSEARCH-004). + * @param SearchShortcutService $searchShortcuts The search shortcuts every + * search box honours + * (REQ-SPX-002). */ public function __construct( IRequest $request, @@ -103,6 +107,7 @@ public function __construct( private readonly DashboardTreeService $treeService, private readonly LoggerInterface $logger, private readonly AdminSettingsService $adminSettingsService, + private readonly SearchShortcutService $searchShortcuts, ) { parent::__construct(appName: Application::APP_ID, request: $request); }//end __construct() @@ -227,6 +232,7 @@ public function index(string $deepLink = ''): TemplateResponse { ->setAllowedWidgets($allowedWidgets) ->setDeepLinkPath($activeState['deepLinkPath']) ->setQuicksearchFallbackTarget($quicksearchFallback) + ->setSearchShortcuts($this->searchShortcuts->getShortcuts()) ->setStartPageWithoutNavigation(($settings['startPageWithoutNavigation'] ?? false) === true) ->apply(); diff --git a/lib/Controller/SearchShortcutsController.php b/lib/Controller/SearchShortcutsController.php new file mode 100644 index 000000000..c6b7b2204 --- /dev/null +++ b/lib/Controller/SearchShortcutsController.php @@ -0,0 +1,131 @@ + + * @copyright 2026 Conduction b.v. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * @version GIT:auto + * @link https://conduction.nl + * + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + */ + +declare(strict_types=1); + +namespace OCA\LaunchPad\Controller; + +use InvalidArgumentException; +use OCA\LaunchPad\AppInfo\Application; +use OCA\LaunchPad\Service\ActionAuthService; +use OCA\LaunchPad\Service\SearchShortcutService; +use OCA\LaunchPad\Settings\LaunchPadAdmin; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\OCS\OCSForbiddenException; +use OCP\IGroupManager; +use OCP\IRequest; +use OCP\IUserSession; + +/** + * Search shortcut administration. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ +class SearchShortcutsController extends Controller { + /** + * Constructor. + * + * @param IRequest $request HTTP request. + * @param SearchShortcutService $shortcuts Shortcut service. + * @param ActionAuthService $actionAuth ADR-023 action authorization. + * @param IUserSession $userSession Current user. + * @param IGroupManager $groupManager Admin check. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + public function __construct( + IRequest $request, + private readonly SearchShortcutService $shortcuts, + private readonly ActionAuthService $actionAuth, + private readonly IUserSession $userSession, + private readonly IGroupManager $groupManager, + ) { + parent::__construct( + appName: Application::APP_ID, + request: $request + ); + }//end __construct() + + /** + * The shortcut list (administrators). + * + * @return JSONResponse `{shortcuts: [...]}`, 401 or 403. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + #[AuthorizedAdminSetting(LaunchPadAdmin::class)] + public function index(): JSONResponse { + $user = $this->userSession->getUser(); + if ($user === null) { + return new JSONResponse(data: ['error' => 'Not authenticated'], statusCode: Http::STATUS_UNAUTHORIZED); + } + + if ($this->groupManager->isAdmin(userId: $user->getUID()) === false) { + return new JSONResponse(data: ['error' => 'Admin required'], statusCode: Http::STATUS_FORBIDDEN); + } + + try { + $this->actionAuth->requireAction($user, 'search-shortcut.list'); + } catch (OCSForbiddenException) { + return new JSONResponse(data: ['error' => 'Forbidden'], statusCode: Http::STATUS_FORBIDDEN); + } + + return new JSONResponse(data: ['shortcuts' => $this->shortcuts->getShortcuts()]); + }//end index() + + /** + * Replace the shortcut list (administrators). + * + * @param array|null $shortcuts The new list. + * + * @return JSONResponse `{shortcuts: [...]}`, 400, 401 or 403. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + #[AuthorizedAdminSetting(LaunchPadAdmin::class)] + public function save(?array $shortcuts = null): JSONResponse { + $user = $this->userSession->getUser(); + if ($user === null) { + return new JSONResponse(data: ['error' => 'Not authenticated'], statusCode: Http::STATUS_UNAUTHORIZED); + } + + if ($this->groupManager->isAdmin(userId: $user->getUID()) === false) { + return new JSONResponse(data: ['error' => 'Admin required'], statusCode: Http::STATUS_FORBIDDEN); + } + + try { + $this->actionAuth->requireAction($user, 'search-shortcut.save'); + } catch (OCSForbiddenException) { + return new JSONResponse(data: ['error' => 'Forbidden'], statusCode: Http::STATUS_FORBIDDEN); + } + + try { + $saved = $this->shortcuts->saveShortcuts(raw: ($shortcuts ?? [])); + } catch (InvalidArgumentException $e) { + return new JSONResponse(data: ['error' => $e->getMessage()], statusCode: Http::STATUS_BAD_REQUEST); + } + + return new JSONResponse(data: ['shortcuts' => $saved]); + }//end save() +}//end class diff --git a/lib/Db/AdminSettingKey.php b/lib/Db/AdminSettingKey.php index d4dff890e..cb87a98e4 100644 --- a/lib/Db/AdminSettingKey.php +++ b/lib/Db/AdminSettingKey.php @@ -16,7 +16,8 @@ * - Footer: FOOTER_ENABLED, FOOTER_HTML, FOOTER_CONFIG, * FOOTER_BACKGROUND_COLOR, FOOTER_TEXT_COLOR * - Setup wizard: SETUP_WIZARD_COMPLETE - * - Quick search: QUICKSEARCH_FALLBACK_TARGET (tile-quick-search) + * - Quick search: QUICKSEARCH_FALLBACK_TARGET (tile-quick-search), + * SEARCH_SHORTCUTS (search-ai-prefix-shortcuts) * * @category Db * @package OCA\LaunchPad\Db @@ -61,5 +62,6 @@ enum AdminSettingKey: string { case MAX_DASHBOARDS_PER_USER = 'max_dashboards_per_user'; case MAX_WIDGETS_PER_DASHBOARD = 'max_widgets_per_dashboard'; case QUICKSEARCH_FALLBACK_TARGET = 'quicksearch_fallback_target'; + case SEARCH_SHORTCUTS = 'search_shortcuts'; case START_PAGE_WITHOUT_NAVIGATION = 'start_page_without_navigation'; }//end enum diff --git a/lib/Service/AdminSettingsService.php b/lib/Service/AdminSettingsService.php index 4c920f324..90996b24f 100644 --- a/lib/Service/AdminSettingsService.php +++ b/lib/Service/AdminSettingsService.php @@ -259,8 +259,10 @@ public function isValidQuicksearchFallbackTarget(?string $value): bool { * * @return bool true when `$value` is a valid `https` URL template * containing `{query}`. + * + * @spec openspec/specs/tile-quick-search/spec.md */ - private function isValidQuicksearchFallbackUrlTemplate(string $value): bool { + public function isValidQuicksearchFallbackUrlTemplate(string $value): bool { if (trim($value) === '' || str_contains(haystack: $value, needle: '{query}') === false) { return false; } diff --git a/lib/Service/InitialStateBuilder.php b/lib/Service/InitialStateBuilder.php index 0322232ff..e3f5bea62 100644 --- a/lib/Service/InitialStateBuilder.php +++ b/lib/Service/InitialStateBuilder.php @@ -338,6 +338,22 @@ public function setQuicksearchFallbackTarget(string $fallbackTarget): self { return $this; }//end setQuicksearchFallbackTarget() + /** + * Set the search shortcuts every search box honours (search-ai-prefix-shortcuts, + * REQ-SPX-002). Optional key, like `quicksearchFallbackTarget`: the JS reader + * defaults it to an empty list. + * + * @param array $shortcuts Validated `{prefix, name, urlTemplate}` entries. + * + * @return self Fluent. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + public function setSearchShortcuts(array $shortcuts): self { + $this->values['searchShortcuts'] = array_values(array: $shortcuts); + return $this; + }//end setSearchShortcuts() + /** * Set whether the dashboard view renders without the left navigation * panel (workspace). diff --git a/lib/Service/SearchShortcutService.php b/lib/Service/SearchShortcutService.php new file mode 100644 index 000000000..b31f43fab --- /dev/null +++ b/lib/Service/SearchShortcutService.php @@ -0,0 +1,150 @@ + + * @copyright 2026 Conduction b.v. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * @link https://conduction.nl + * + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + */ + +declare(strict_types=1); + +namespace OCA\LaunchPad\Service; + +use InvalidArgumentException; +use OCA\LaunchPad\Db\AdminSettingKey; +use OCA\LaunchPad\Db\AdminSettingMapper; + +/** + * Read and validate the search shortcut list. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ +class SearchShortcutService { + /** + * Most shortcuts one instance may define. + * + * @var int + */ + public const MAX_SHORTCUTS = 30; + + /** + * A prefix: `!` and 1 to 10 letters or digits. + * + * @var string + */ + public const PREFIX_PATTERN = '/^![a-z0-9]{1,10}$/'; + + /** + * Constructor. + * + * @param AdminSettingMapper $settingMapper Admin settings store. + * @param AdminSettingsService $adminSettings Holds the https `{query}` template check. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + public function __construct( + private readonly AdminSettingMapper $settingMapper, + private readonly AdminSettingsService $adminSettings, + ) { + }//end __construct() + + /** + * The stored shortcuts. A stored list that no longer validates reads as + * empty, so the page never receives an address it must not open. + * + * @return array + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + public function getShortcuts(): array { + $raw = $this->settingMapper->getValue(key: AdminSettingKey::SEARCH_SHORTCUTS->value, default: []); + if (is_array(value: $raw) === false) { + return []; + } + + try { + return $this->validate(raw: $raw); + } catch (InvalidArgumentException) { + return []; + } + }//end getShortcuts() + + /** + * Validate and store the shortcuts. + * + * @param array $raw Shortcuts as sent by the admin screen. + * + * @return array The stored list. + * + * @throws InvalidArgumentException When an entry is invalid. + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + public function saveShortcuts(array $raw): array { + $shortcuts = $this->validate(raw: $raw); + $this->settingMapper->setSetting(key: AdminSettingKey::SEARCH_SHORTCUTS->value, value: $shortcuts); + + return $shortcuts; + }//end saveShortcuts() + + /** + * Check and normalise the list. + * + * @param array $raw Raw entries. + * + * @return array + * + * @throws InvalidArgumentException When an entry is invalid. + */ + private function validate(array $raw): array { + if (count(value: $raw) > self::MAX_SHORTCUTS) { + throw new InvalidArgumentException(message: 'At most ' . self::MAX_SHORTCUTS . ' search shortcuts'); + } + + $result = []; + $seen = []; + foreach (array_values(array: $raw) as $entry) { + if (is_array(value: $entry) === false) { + throw new InvalidArgumentException(message: 'A search shortcut must be an object'); + } + + $prefix = mb_strtolower(string: trim(string: (string)($entry['prefix'] ?? ''))); + if (preg_match(pattern: self::PREFIX_PATTERN, subject: $prefix) !== 1) { + throw new InvalidArgumentException(message: 'A prefix is ! followed by 1 to 10 letters or digits: ' . $prefix); + } + + if (isset($seen[$prefix]) === true) { + throw new InvalidArgumentException(message: 'Prefix used twice: ' . $prefix); + } + + $name = trim(string: (string)($entry['name'] ?? '')); + if ($name === '' || mb_strlen(string: $name) > 64) { + throw new InvalidArgumentException(message: 'A shortcut name must be 1 to 64 characters: ' . $prefix); + } + + $template = trim(string: (string)($entry['urlTemplate'] ?? '')); + if ($this->adminSettings->isValidQuicksearchFallbackUrlTemplate(value: $template) === false) { + throw new InvalidArgumentException(message: 'Use an https address that contains {query}: ' . $prefix); + } + + $seen[$prefix] = true; + $result[] = ['prefix' => $prefix, 'name' => $name, 'urlTemplate' => $template]; + }//end foreach + + return $result; + }//end validate() +}//end class diff --git a/lib/actions.seed.json b/lib/actions.seed.json index 2f8084b04..563e84597 100644 --- a/lib/actions.seed.json +++ b/lib/actions.seed.json @@ -60,6 +60,8 @@ "metadata-admin.update-field": ["admin"], "metadata-admin.delete-field": ["admin"], "people-widget.get-users": ["admin"], + "search-shortcut.list": ["admin"], + "search-shortcut.save": ["admin"], "profile-fields.get-own": ["admin", "@all"], "profile-fields.save-own": ["admin", "@all"], "profile-fields.get-definitions": ["admin"], diff --git a/openspec/changes/launcher-tile-sorting/.openspec.yaml b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/.openspec.yaml similarity index 100% rename from openspec/changes/launcher-tile-sorting/.openspec.yaml rename to openspec/changes/archive/2026-09-30-launcher-tile-sorting/.openspec.yaml diff --git a/openspec/changes/launcher-tile-sorting/design.md b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/design.md similarity index 78% rename from openspec/changes/launcher-tile-sorting/design.md rename to openspec/changes/archive/2026-09-30-launcher-tile-sorting/design.md index 7f618ccd3..65e8ec012 100644 --- a/openspec/changes/launcher-tile-sorting/design.md +++ b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/design.md @@ -43,3 +43,11 @@ A reflow changes the reading order, so the container announces "Sorted by most u - Vitest: ordering for each mode, tie breaks, the once-per-load shuffle, storage blocked, edit mode shows stored positions. - Playwright: set "Most used", click one tile three times, reload, it comes first; switch back to "By hand", the original layout returns. + +## What the build corrected (2026-09-30) + +- Tiles inside a container render through the widget registry, not through `TileWidget.vue`, so a click there never reached `recordTileClick()`. The container counts a click on any of its children itself (`onChildClick`), in the browser only; `recordTileClick()` and its server call are unchanged. +- The widget menu is an editor tool, so "Forget my usage" is a small button under the container title, shown in view mode for most used and last used when there is use to forget. +- A new click does not re-sort until the next page load, so tiles never jump under the pointer. "Forget my usage" re-sorts at once and rebuilds the inner grid. +- When browser storage is blocked, most used and last used show the stored order (the spec's rule), not alphabetical. +- A view-time reflow never reaches the persistence callback: `onGridChange` returns in view mode. diff --git a/openspec/changes/launcher-tile-sorting/proposal.md b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/proposal.md similarity index 100% rename from openspec/changes/launcher-tile-sorting/proposal.md rename to openspec/changes/archive/2026-09-30-launcher-tile-sorting/proposal.md diff --git a/openspec/changes/launcher-tile-sorting/specs/container-widget/spec.md b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/specs/container-widget/spec.md similarity index 100% rename from openspec/changes/launcher-tile-sorting/specs/container-widget/spec.md rename to openspec/changes/archive/2026-09-30-launcher-tile-sorting/specs/container-widget/spec.md diff --git a/openspec/changes/launcher-tile-sorting/tasks.md b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/tasks.md similarity index 58% rename from openspec/changes/launcher-tile-sorting/tasks.md rename to openspec/changes/archive/2026-09-30-launcher-tile-sorting/tasks.md index 5a4aba73f..4412f4624 100644 --- a/openspec/changes/launcher-tile-sorting/tasks.md +++ b/openspec/changes/archive/2026-09-30-launcher-tile-sorting/tasks.md @@ -1,8 +1,8 @@ # Tasks: launcher-tile-sorting -- [ ] 1. Add `ContainerWidgetForm.vue` with the three communal fields plus "Sort tiles", and register it in `FORM_OVERRIDES` in `src/constants/widgetRegistry.js`. Verify: Vitest on the form. -- [ ] 2. Keep local `{count, lastUsedAt}` per placement in `src/composables/useTileClickTracking.js`, storage failures caught. Verify: Vitest with storage available and blocked. -- [ ] 3. Order and reflow children in view mode in `ContainerWidget.vue` for the five modes, with the tie rules and a once-per-load shuffle. Verify: Vitest per mode. -- [ ] 4. Add "Forget my usage" to the container menu and the "Sorted by" accessible description. Verify: Vitest and an axe check. -- [ ] 5. Playwright: most used moves a clicked tile to the front after reload; "By hand" restores the stored layout. -- [ ] 6. Add `nl` and `en` strings, fold the delta into `openspec/specs/container-widget/spec.md` on archive, and set d-tile-sort and tile-most-used to `built` in the parity matrix with evidence lines. +- [x] 1. Add `ContainerWidgetForm.vue` with the three communal fields plus "Sort tiles", and register it in `FORM_OVERRIDES` in `src/constants/widgetRegistry.js`. Verify: Vitest on the form. +- [x] 2. Keep local `{count, lastUsedAt}` per placement in `src/composables/useTileClickTracking.js`, storage failures caught. Verify: Vitest with storage available and blocked. +- [x] 3. Order and reflow children in view mode in `ContainerWidget.vue` for the five modes, with the tie rules and a once-per-load shuffle. Verify: Vitest per mode. +- [x] 4. Add "Forget my usage" to the container menu and the "Sorted by" accessible description. Verify: Vitest and an axe check. +- [ ] 5. Playwright: most used moves a clicked tile to the front after reload; "By hand" restores the stored layout. Not written: the requirements carry `@e2e exclude` naming the Vitest files that cover them. +- [x] 6. Add `nl` and `en` strings, fold the delta into `openspec/specs/container-widget/spec.md` on archive, and set d-tile-sort and tile-most-used to `built` in the parity matrix with evidence lines. diff --git a/openspec/changes/search-ai-prefix-shortcuts/.openspec.yaml b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/.openspec.yaml similarity index 100% rename from openspec/changes/search-ai-prefix-shortcuts/.openspec.yaml rename to openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/.openspec.yaml diff --git a/openspec/changes/search-ai-prefix-shortcuts/design.md b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/design.md similarity index 79% rename from openspec/changes/search-ai-prefix-shortcuts/design.md rename to openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/design.md index fdc114c03..cba698c48 100644 --- a/openspec/changes/search-ai-prefix-shortcuts/design.md +++ b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/design.md @@ -36,3 +36,10 @@ Only administrators edit the list. Every user can use it. The query leaves the b - PHPUnit: prefix format, uniqueness, template check, the 30-entry limit. - Vitest: `parsePrefix` cases (known, unknown, prefix only, uppercase), result rendering, Enter opening the right address, `?` listing. - Playwright: with `!t` set to a test site, typing `!t printer` and Enter opens that site with `printer`. + +## What the build corrected (2026-09-30) + +- The Workspace quick search and the search widget are one component since the bar became a widget (`SearchWidget.vue` hosts `RuntimeShellSearch.vue`), so the shortcuts are wired once. +- The shortcut list has its own admin endpoints (`GET`/`PUT /api/admin/search-shortcuts`, `SearchShortcutService`) instead of another parameter on `AdminSettingsService::updateSettings()`; the https `{query}` check is the same method, made public. +- Prefixes are stored lower case. Enter on a prefix with nothing after it opens nothing, and a shortcut query dims no tiles. +- Enter on an entry of the `?` list types that prefix into the box. diff --git a/openspec/changes/search-ai-prefix-shortcuts/proposal.md b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/proposal.md similarity index 100% rename from openspec/changes/search-ai-prefix-shortcuts/proposal.md rename to openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/proposal.md diff --git a/openspec/changes/search-ai-prefix-shortcuts/specs/tile-quick-search/spec.md b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/specs/tile-quick-search/spec.md similarity index 100% rename from openspec/changes/search-ai-prefix-shortcuts/specs/tile-quick-search/spec.md rename to openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/specs/tile-quick-search/spec.md diff --git a/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/tasks.md b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/tasks.md new file mode 100644 index 000000000..c9ce6620c --- /dev/null +++ b/openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts/tasks.md @@ -0,0 +1,9 @@ +# Tasks: search-ai-prefix-shortcuts + +- [x] 1. Add `search_shortcuts` to `lib/Db/AdminSettingKey.php` and validate it in `AdminSettingsService` (prefix pattern, uniqueness, template check, limit 30). Verify: PHPUnit. +- [x] 2. Provide the list through `lib/Service/InitialStateBuilder.php`. Verify: initial-state contract test. +- [x] 3. Add `parsePrefix()` to `src/composables/useTileSearch.js` and route a match ahead of ranking. Verify: Vitest. +- [x] 4. Render the "Search for " result and the `?` list in `SearchWidget.vue` and `RuntimeShellSearch.vue`, opening through `useTileSearchHost.js`. Verify: Vitest and an axe check on the list. +- [x] 5. Add the admin section to edit shortcuts. Verify: Vitest. +- [ ] 6. Playwright: `!t printer` opens the configured site. Not written: the requirements carry `@e2e exclude` naming the PHPUnit and Vitest files that cover them. +- [x] 7. Add `nl` and `en` strings, fold the delta into `openspec/specs/tile-quick-search/spec.md` on archive, and set d-search-prefix to `built` in the parity matrix with evidence lines. diff --git a/openspec/changes/search-ai-prefix-shortcuts/tasks.md b/openspec/changes/search-ai-prefix-shortcuts/tasks.md deleted file mode 100644 index 89b996644..000000000 --- a/openspec/changes/search-ai-prefix-shortcuts/tasks.md +++ /dev/null @@ -1,9 +0,0 @@ -# Tasks: search-ai-prefix-shortcuts - -- [ ] 1. Add `search_shortcuts` to `lib/Db/AdminSettingKey.php` and validate it in `AdminSettingsService` (prefix pattern, uniqueness, template check, limit 30). Verify: PHPUnit. -- [ ] 2. Provide the list through `lib/Service/InitialStateBuilder.php`. Verify: initial-state contract test. -- [ ] 3. Add `parsePrefix()` to `src/composables/useTileSearch.js` and route a match ahead of ranking. Verify: Vitest. -- [ ] 4. Render the "Search for " result and the `?` list in `SearchWidget.vue` and `RuntimeShellSearch.vue`, opening through `useTileSearchHost.js`. Verify: Vitest and an axe check on the list. -- [ ] 5. Add the admin section to edit shortcuts. Verify: Vitest. -- [ ] 6. Playwright: `!t printer` opens the configured site. -- [ ] 7. Add `nl` and `en` strings, fold the delta into `openspec/specs/tile-quick-search/spec.md` on archive, and set d-search-prefix to `built` in the parity matrix with evidence lines. diff --git a/openspec/parity/capabilities.json b/openspec/parity/capabilities.json index db13b4c6e..49e169e15 100644 --- a/openspec/parity/capabilities.json +++ b/openspec/parity/capabilities.json @@ -2095,11 +2095,12 @@ "homarr": "no", "dashy": "yes", "built": { - "state": "specified", + "state": "built", + "evidence": "src/components/Widgets/Renderers/ContainerWidget.vue (displayChildren); src/utils/sortContainerTiles.js; src/components/Widgets/Renderers/ContainerWidgetForm.vue; src/composables/useTileClickTracking.js (readLocalTileUse)", "owner": "ConductionNL/launchpad", - "change": "openspec/changes/launcher-tile-sorting" + "change": "openspec/changes/archive/2026-09-30-launcher-tile-sorting" }, - "reachedOn": "nothing", + "reachedOn": "A container widget: Edit > Sort tiles (by hand, alphabetically, most used, last used, at random); the order applies when the dashboard is viewed", "evidence": { "workspace-365": "https://support.workspace365.net/en/articles/175612-faq-workspace-365-v4 'Frequently used apps' moved to the navigation menu (read 2026-09-26)", "nextcloud-dashboard": "source read at v35.0.1: the app menu order is set by hand (apps/theming/src/components/UserSectionAppMenu.vue:133-135, AdminSectionAppMenu.vue:86-90); searched apps/dashboard and apps/theming for usage, frequent, most used: none. The shipped recommendations widget suggests files, not apps", @@ -6217,11 +6218,12 @@ "homarr": "yes", "dashy": "yes", "built": { - "state": "specified", + "state": "built", + "evidence": "lib/Service/SearchShortcutService.php; src/composables/useTileSearch.js (parsePrefix); src/components/RuntimeShellSearch.vue; src/components/admin/tabs/SearchShortcutsTab.vue", "owner": "ConductionNL/launchpad", - "change": "openspec/changes/search-ai-prefix-shortcuts" + "change": "openspec/changes/archive/2026-09-30-search-ai-prefix-shortcuts" }, - "reachedOn": "nothing", + "reachedOn": "Any search widget: type !t printer (a prefix the administrator set under Admin settings > LaunchPad > Search shortcuts) or ? for the list", "evidence": { "workspace-365": "https://support.workspace365.net/en/articles/175653-global-search users 'search by category' (Announcements, Apps, Email, Files, Folders, Knowledge articles, People, plus OpenIMS or Egnyte when configured); no prefix syntax to send a query to another site (read 2026-09-26)", "microsoft-viva": "not checked: Microsoft 365 roadmap 562015 'Scoped Search for SharePoint app in Teams' scopes search to SharePoint global search (cached roadmap, read 2026-09-26); no prefix to an external site found", @@ -6288,11 +6290,12 @@ "homarr": "no", "dashy": "yes", "built": { - "state": "specified", + "state": "built", + "evidence": "src/components/Widgets/Renderers/ContainerWidget.vue (displayChildren); src/utils/sortContainerTiles.js; src/components/Widgets/Renderers/ContainerWidgetForm.vue; src/composables/useTileClickTracking.js (readLocalTileUse)", "owner": "ConductionNL/launchpad", - "change": "openspec/changes/launcher-tile-sorting" + "change": "openspec/changes/archive/2026-09-30-launcher-tile-sorting" }, - "reachedOn": "nothing", + "reachedOn": "A container widget: Edit > Sort tiles (by hand, alphabetically, most used, last used, at random); the order applies when the dashboard is viewed", "evidence": { "workspace-365": "not checked: user guide 175363 and FAQ for admins 175608 describe drag and drop reordering and automatic gap filling, no sort by name, last use or random (read 2026-09-26)", "microsoft-viva": "not checked: https://learn.microsoft.com/en-us/sharepoint/homesites/available-dashboard-cards card order is set by the author and users drag cards; sort options exist only inside Folder (date, A-Z) and Stream playlist cards, not for cards (read 2026-09-26)", diff --git a/openspec/specs/container-widget/spec.md b/openspec/specs/container-widget/spec.md index a75afa972..1c6deeb07 100644 --- a/openspec/specs/container-widget/spec.md +++ b/openspec/specs/container-widget/spec.md @@ -18,6 +18,7 @@ The `content` object carries these fields: - **backgroundColor** (string, default `'transparent'`) — CSS colour applied to the container wrapper. - **padding** (`'none' | 'small' | 'medium' | 'large'`, default `'medium'`) — preset spacing between the wrapper edge and the inner grid. - **title** (string, default `''`) — optional heading rendered above the inner grid; empty means no heading. +- **sortBy** (`'manual' | 'alphabetical' | 'most-used' | 'last-used' | 'random'`, default `'manual'`): the view-time order of the children (REQ-TSO-001). Never changes the stored positions. ## Inner-grid configuration @@ -124,17 +125,79 @@ Containers MAY hold containers, but the maximum nesting depth is **3 levels**. T ### Requirement: REQ-CONT-007 Form fields -The container's add/edit form MUST collect three fields, all optional: +The container's add and edit form MUST collect four fields, all optional: -- `backgroundColor` — hex colour (NcColorPicker), default `'transparent'` -- `padding` — enum `'none' | 'small' | 'medium' | 'large'` (NcSelect), default `'medium'` -- `title` — string (NcTextField), default `''` (no title rendered when empty) +- `backgroundColor`: hex colour (NcColorPicker), default `'transparent'` +- `padding`: enum `'none' | 'small' | 'medium' | 'large'` (NcSelect), default `'medium'` +- `title`: string (NcTextField), default `''` (no title rendered when empty) +- `sortBy`: enum `'manual' | 'alphabetical' | 'most-used' | 'last-used' | 'random'` (NcSelect with an input label), default `'manual'` -Children are NOT managed via this form — they're added/removed/moved via the inner grid's own affordances when the container is in edit mode. +Children are NOT managed via this form. They are added, removed and moved through the inner grid's own affordances when the container is in edit mode. #### Scenario: Form has three fields - **GIVEN** the container form is mounted - **WHEN** rendered -- **THEN** exactly three input controls MUST be present: backgroundColor picker, padding select, title text field +- **THEN** the three communal input controls MUST be present: background colour picker, padding select and title text field +- **AND** LaunchPad's form MUST add exactly one more control, the "Sort tiles" select - **AND** no "manage children" UI MUST be in the form + +### Requirement: A container sorts its tiles when viewed (REQ-TSO-001) + +@e2e exclude Playwright is not wired for container sorting yet; covered by src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js and ContainerWidgetForm.spec.js + +In view mode a container MUST order its children by its `sortBy` setting and reflow them row by row into its inner grid, keeping each child's size. It MUST NOT write the new positions back. In edit mode it MUST show the stored positions. + +#### Scenario: Alphabetical order + +- **GIVEN** a container "Applicaties" on Pieter's dashboard with tiles "Zaaksysteem", "Agenda" and "Mail" placed in that order, and "Sort tiles" set to "Alphabetically" +- **WHEN** Pieter opens the Workspace page +- **THEN** he sees "Agenda", "Mail", "Zaaksysteem" from left to right + +#### Scenario: Back to the author's layout + +- **GIVEN** the same container sorted alphabetically +- **WHEN** the author sets "Sort tiles" to "By hand" and saves +- **THEN** the tiles show in their stored order "Zaaksysteem", "Agenda", "Mail" + +### Requirement: Most used and last used read the viewer's own browser (REQ-TSO-002) + +@e2e exclude Playwright is not wired for container sorting yet; covered by src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js and ContainerWidgetForm.spec.js + +Most used and last used MUST order tiles by counts and times kept in the viewer's browser storage, which MUST NOT be sent to the server. Tiles with equal counts MUST fall back to alphabetical order. When browser storage is unavailable the container MUST show the stored order without an error. + +#### Scenario: A daily app moves to the front + +- **GIVEN** a container sorted "Most used" with tiles "Agenda", "Mail" and "Zaaksysteem", and Pieter has never clicked any of them +- **WHEN** he opens "Zaaksysteem" three times and reloads the Workspace page +- **THEN** "Zaaksysteem" is the first tile in the container + +#### Scenario: Counts stay on the device + +- **GIVEN** Pieter clicks tiles in a container sorted "Most used" +- **WHEN** the clicks are recorded +- **THEN** no request carries his per-tile counts, and the only server call per click is the existing anonymous `POST /api/tile-click/{placementId}` + +### Requirement: Viewers can forget their usage (REQ-TSO-003) + +@e2e exclude Playwright is not wired for container sorting yet; covered by src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js and ContainerWidgetForm.spec.js + +A container sorted by most used or last used MUST offer "Forget my usage" in its menu, which clears the viewer's local counts for that container's tiles. + +#### Scenario: Forget my usage + +- **GIVEN** "Zaaksysteem" leads Pieter's "Most used" container +- **WHEN** he chooses "Forget my usage" in the container menu +- **THEN** the tiles show in alphabetical order until he clicks one again + +### Requirement: Random order holds for the page load (REQ-TSO-004) + +@e2e exclude Playwright is not wired for container sorting yet; covered by src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js and ContainerWidgetForm.spec.js + +Random order MUST shuffle once per page load and keep that order until the page is reloaded. + +#### Scenario: No jumping tiles + +- **GIVEN** a container sorted "At random" +- **WHEN** Pieter opens the Workspace page and a widget elsewhere refreshes its data +- **THEN** the tile order in the container stays the same until he reloads diff --git a/openspec/specs/tile-quick-search/spec.md b/openspec/specs/tile-quick-search/spec.md index b92fe85ae..2fa97ffc9 100644 --- a/openspec/specs/tile-quick-search/spec.md +++ b/openspec/specs/tile-quick-search/spec.md @@ -251,3 +251,52 @@ cannot see on screen, or cannot find tiles they can. - WHEN its title is resolved - THEN the widget type's display name MUST be used - AND the generic fallback word MUST be used only when nothing else resolves + +### Requirement: Administrators define search shortcuts (REQ-SPX-001) + +@e2e exclude Playwright is not wired for search shortcuts yet; covered by tests/Unit/Service/SearchShortcutServiceTest.php and src/components/__tests__/RuntimeShellSearch.shortcuts.spec.js + +An administrator MUST be able to define up to 30 search shortcuts, each with a prefix (`!` and 1 to 10 letters or digits, unique regardless of case), a name, and an https address containing `{query}`. The server MUST refuse an entry that breaks any of these rules. + +#### Scenario: Add the TOPdesk shortcut + +- **GIVEN** Noor is an administrator on the LaunchPad admin page, section "Search shortcuts" +- **WHEN** she adds prefix `!t`, name "TOPdesk" and address `https://topdesk.gemeente.nl/tas/secure/search?q={query}` and saves +- **THEN** the shortcut is listed + +#### Scenario: Plain http is refused + +- **GIVEN** Noor adds a shortcut +- **WHEN** she enters the address `http://intranet.gemeente.nl/zoek?q={query}` +- **THEN** the save fails with "Use an https address that contains {query}" + +### Requirement: A prefix sends the query to its site (REQ-SPX-002) + +@e2e exclude Playwright is not wired for search shortcuts yet; covered by tests/Unit/Service/SearchShortcutServiceTest.php and src/components/__tests__/RuntimeShellSearch.shortcuts.spec.js + +In the search widget and the Workspace quick search, a query whose first word is a known prefix MUST show a single result naming the site and the rest of the query, and Enter MUST open the site's address with the URL-encoded rest in a new tab. A query without a known prefix MUST behave as before: tile filtering, then the configured fallback. + +#### Scenario: Search TOPdesk + +- **GIVEN** the shortcut `!t` for "TOPdesk" +- **WHEN** Pieter types `!t printer 3e verdieping` in the quick search +- **THEN** the only result reads "Search TOPdesk for printer 3e verdieping" +- **AND WHEN** he presses Enter, a new tab opens `https://topdesk.gemeente.nl/tas/secure/search?q=printer%203e%20verdieping` + +#### Scenario: Unknown prefix falls through + +- **GIVEN** no shortcut `!x` +- **WHEN** Pieter types `!x rapport` +- **THEN** the box filters tiles for `!x rapport` and applies the normal fallback + +### Requirement: Users can see the shortcuts (REQ-SPX-003) + +@e2e exclude Playwright is not wired for search shortcuts yet; covered by tests/Unit/Service/SearchShortcutServiceTest.php and src/components/__tests__/RuntimeShellSearch.shortcuts.spec.js + +Typing `!` alone or `?` in a search box MUST list the available shortcuts with their prefix and name, announced to screen readers. + +#### Scenario: List the shortcuts + +- **GIVEN** shortcuts `!t` "TOPdesk" and `!z` "Zaaksysteem" +- **WHEN** Pieter types `?` in the search widget +- **THEN** he sees "!t TOPdesk" and "!z Zaaksysteem" diff --git a/src/components/RuntimeShellSearch.vue b/src/components/RuntimeShellSearch.vue index 58b81237e..282d05d08 100644 --- a/src/components/RuntimeShellSearch.vue +++ b/src/components/RuntimeShellSearch.vue @@ -37,7 +37,7 @@ :id="listboxId" class="runtime-shell-search__results" role="listbox" - :aria-label="t('launchpad', 'Matching tiles')"> + :aria-label="listboxLabel"> @@ -179,6 +179,14 @@ export default { type: String, default: '', }, + + /** + * The admin's search shortcuts `{prefix, name, urlTemplate}` (REQ-SPX-002). + */ + shortcuts: { + type: Array, + default: () => [], + }, }, emits: ['open', 'filter', 'fallback', 'clear'], @@ -197,6 +205,7 @@ export default { this.$emit('fallback', action) }, getFallbackTarget: () => this.fallbackTarget, + getShortcuts: () => this.shortcuts, }), instanceId: `runtime-shell-search-${(instanceCounter += 1)}`, @@ -239,6 +248,22 @@ export default { * @spec openspec/specs/tile-quick-search/spec.md#req-qsearch-003 * @return {string|null} */ + /** + * Whether the results are shortcuts rather than tiles (REQ-SPX-002/003). + * + * @spec openspec/specs/tile-quick-search/spec.md + */ + showsShortcuts() { + return Boolean(this.search.state.results[0]?.kind) + }, + + /** @spec openspec/specs/tile-quick-search/spec.md */ + listboxLabel() { + return this.showsShortcuts + ? this.t('launchpad', 'Search shortcuts') + : this.t('launchpad', 'Matching tiles') + }, + ariaControls() { return this.hasResults ? this.listboxId : null }, @@ -290,6 +315,11 @@ export default { if (count === 0) { return this.noResultsMessage } + if (this.showsShortcuts) { + return this.search.state.results + .map((result) => this.resultLabel(result)) + .join(', ') + } return this.n( 'launchpad', '%n matching tile', @@ -329,6 +359,11 @@ export default { }, watch: { + /** @spec openspec/specs/tile-quick-search/spec.md */ + shortcuts() { + this.search.setItems(this.items) + }, + items: { immediate: true, /** @@ -376,6 +411,28 @@ export default { * @param {number} index the option's index in `results`. * @return {string} */ + /** + * The visible text of a result: a tile's label, a listed shortcut, + * or where a shortcut will search (REQ-SPX-002/003). + * + * @param {object} result A result from the composable. + * @return {string} The text. + * @spec openspec/specs/tile-quick-search/spec.md + */ + resultLabel(result) { + if (result.kind === 'shortcut') { + return result.rest + ? this.t('launchpad', 'Search {name} for {query}', { + name: result.shortcut.name, + query: result.rest, + }) + : this.t('launchpad', 'Type what to search for in {name}', { + name: result.shortcut.name, + }) + } + return result.label + }, + optionId(index) { return `${this.listboxId}-option-${index}` }, @@ -443,7 +500,8 @@ export default { */ emitFilter() { const query = this.search.state.query - if (!query) { + // A shortcut query is not a tile filter: no tile is dimmed. + if (!query || this.showsShortcuts) { this.$emit('filter', null) return } diff --git a/src/components/Widgets/Renderers/ContainerWidget.vue b/src/components/Widgets/Renderers/ContainerWidget.vue index 1296f97d2..906bd0cf8 100644 --- a/src/components/Widgets/Renderers/ContainerWidget.vue +++ b/src/components/Widgets/Renderers/ContainerWidget.vue @@ -8,16 +8,27 @@

{{ titleText }}

+ -
+
+ :gs-h="child.gridHeight || 2" + :data-use-key="useKey(child)">
@@ -30,8 +41,21 @@ import ContainerChild from './ContainerChild.vue' import { getNestedGridOptions, + NESTED_COLUMNS, useNestedGridManager, } from '../../../composables/useNestedGridManager.js' +import { + forgetLocalTileUse, + randomRankFor, + readLocalTileUse, + recordLocalTileUse, +} from '../../../composables/useTileClickTracking.js' +import { + childTitle, + orderChildren, + reflowChildren, + SORT_MODES, +} from '../../../utils/sortContainerTiles.js' const PADDING_TOKENS = Object.freeze({ none: '0', @@ -81,6 +105,8 @@ export default { data() { return { gridInstance: null, + // The viewer's own tile use, read once per page load (REQ-TSO-002). + use: readLocalTileUse(), } }, @@ -91,6 +117,77 @@ export default { return Array.isArray(list) ? list : [] }, + /** @spec openspec/specs/container-widget/spec.md */ + sortBy() { + const value = this.content?.sortBy + return SORT_MODES.includes(value) ? value : 'manual' + }, + + /** + * The children as shown: the stored layout in edit mode or by hand, + * otherwise ordered and reflowed at view time (REQ-TSO-001). + * + * @spec openspec/specs/container-widget/spec.md + */ + displayChildren() { + if (this.editMode || this.sortBy === 'manual') { + return this.children + } + // REQ-TSO-002: without browser storage there is no use to sort by. + if ( + this.use === null + && ['most-used', 'last-used'].includes(this.sortBy) + ) { + return this.children + } + const ordered = orderChildren(this.children, this.sortBy, { + use: this.use || {}, + keyOf: this.useKey, + rankOf: randomRankFor, + collator: new Intl.Collator(undefined, { + sensitivity: 'base', + numeric: true, + }), + }) + return reflowChildren(ordered, NESTED_COLUMNS) + }, + + /** @spec openspec/specs/container-widget/spec.md */ + sortDescription() { + const labels = { + alphabetical: t('launchpad', 'Sorted alphabetically'), + 'most-used': t('launchpad', 'Sorted by most used'), + 'last-used': t('launchpad', 'Sorted by last used'), + random: t('launchpad', 'Sorted at random'), + } + return this.editMode ? '' : labels[this.sortBy] || '' + }, + + /** + * Offer "Forget my usage" when the order reads the viewer's use and + * there is use to forget (REQ-TSO-003). + * + * @spec openspec/specs/container-widget/spec.md + */ + canForget() { + if (this.editMode || !['most-used', 'last-used'].includes(this.sortBy)) { + return false + } + return ( + this.use !== null + && this.children.some((child) => this.use[this.useKey(child)]) + ) + }, + + /** @spec openspec/specs/container-widget/spec.md */ + layoutSignature() { + return this.displayChildren + .map( + (child) => `${this.useKey(child)}@${child.gridX},${child.gridY}`, + ) + .join('|') + }, + /** @spec openspec/specs/container-widget/spec.md */ backgroundColor() { const value = this.content?.backgroundColor @@ -137,15 +234,83 @@ export default { }, }, + watch: { + /** + * A new view-time layout (after "Forget my usage") rebuilds the + * inner grid, which reads positions only when it starts. + * + * @spec openspec/specs/container-widget/spec.md + */ + layoutSignature() { + if (this.editMode || !this.gridInstance) { + return + } + this.destroyInnerGrid() + this.$nextTick(() => this.initInnerGrid()) + }, + }, + + /** @spec openspec/specs/container-widget/spec.md */ mounted() { + // REQ-TSO-002: one delegated listener counts clicks on the tiles' + // own links and buttons; the wrapper itself is not a control. + this.$refs.innerGrid?.addEventListener('click', this.onGridClick) this.initInnerGrid() }, + /** @spec openspec/specs/container-widget/spec.md */ beforeUnmount() { + this.$refs.innerGrid?.removeEventListener('click', this.onGridClick) this.destroyInnerGrid() }, methods: { + /** + * The key a child's use is stored under: its placement id, else its + * uuid, else its title. + * + * @param {object} child The child placement. + * @return {string} The key. + * @spec openspec/specs/container-widget/spec.md + */ + useKey(child) { + if (child?.id !== undefined && child?.id !== null) { + return String(child.id) + } + if (typeof child?.uuid === 'string' && child.uuid !== '') { + return child.uuid + } + return `title:${childTitle(child)}` + }, + + /** + * Count a click on a tile in this browser (REQ-TSO-002). The order + * does not change until the next page load, so tiles never jump + * under the pointer. + * + * @param {MouseEvent} event The click inside the grid. + * @spec openspec/specs/container-widget/spec.md + */ + onGridClick(event) { + if (this.editMode) { + return + } + const item = event?.target?.closest?.('[data-use-key]') + if (item) { + recordLocalTileUse(item.dataset.useKey) + } + }, + + /** + * Forget this browser's use of this container's tiles (REQ-TSO-003). + * + * @spec openspec/specs/container-widget/spec.md + */ + forgetUsage() { + forgetLocalTileUse(this.children.map((child) => this.useKey(child))) + this.use = readLocalTileUse() + }, + /** * Stable key for v-for over child placements. Falls back to the * loop index when a child has neither id nor uuid, which keeps @@ -222,7 +387,8 @@ export default { * @spec openspec/specs/container-widget/spec.md */ onGridChange(_event, nodes) { - if (!Array.isArray(nodes) || nodes.length === 0) { + // A view-time reflow is never written back (REQ-TSO-001). + if (!this.editMode || !Array.isArray(nodes) || nodes.length === 0) { return } const byKey = new Map() @@ -309,6 +475,18 @@ export default { min-height: 0; } +.container-widget__forget { + align-self: flex-start; + padding: 2px 8px; + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + background: var(--color-background-hover); + color: var(--color-main-text); + font-size: 12px; + cursor: pointer; + pointer-events: auto; +} + .container-widget__child { /* REQ-CONT-004: child wrappers re-enable pointer events so clicks on a child fire the child's own handlers — the container itself diff --git a/src/components/Widgets/Renderers/ContainerWidgetForm.vue b/src/components/Widgets/Renderers/ContainerWidgetForm.vue new file mode 100644 index 000000000..ca34e6768 --- /dev/null +++ b/src/components/Widgets/Renderers/ContainerWidgetForm.vue @@ -0,0 +1,139 @@ + + + + + + + diff --git a/src/components/Widgets/Renderers/SearchWidget.vue b/src/components/Widgets/Renderers/SearchWidget.vue index b48e54256..b0156959f 100644 --- a/src/components/Widgets/Renderers/SearchWidget.vue +++ b/src/components/Widgets/Renderers/SearchWidget.vue @@ -9,6 +9,7 @@ :items="searchableTiles" :placeholder="content.placeholder || ''" :fallbackTarget="effectiveFallbackTarget" + :shortcuts="injectedSearchShortcuts" @open="onSearchOpen" @filter="onSearchFilter" @fallback="onSearchFallback" @@ -64,6 +65,15 @@ export default { from: 'quicksearchFallbackTarget', default: 'none', }, + + /** + * The admin's search shortcuts (REQ-SPX-002). An empty list keeps + * the box as it was. + */ + injectedSearchShortcuts: { + from: 'searchShortcuts', + default: () => [], + }, }, props: { diff --git a/src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js b/src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js new file mode 100644 index 000000000..20d0fe9e6 --- /dev/null +++ b/src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js @@ -0,0 +1,226 @@ +/** + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * Vitest unit tests for tile sorting in `ContainerWidget.vue` + * (launcher-tile-sorting, REQ-TSO-001..004): the view-time order per mode, + * the reflow that keeps sizes and never writes back, most and last used read + * from the viewer's browser only, "Forget my usage", and the once-per-load + * shuffle. + */ + +import { mount } from '@vue/test-utils' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import ContainerWidget from '../ContainerWidget.vue' +import { + __resetTileUseForTest, + recordLocalTileUse, +} from '../../../../composables/useTileClickTracking.js' + +vi.mock('@nextcloud/axios', () => ({ + default: { + get: vi.fn(() => Promise.resolve({ data: {} })), + post: vi.fn(() => Promise.resolve({})), + }, +})) + +function tile(id, title, x) { + return { + id, + type: 'tile', + content: { title }, + gridX: x, + gridY: 0, + gridWidth: 1, + gridHeight: 1, + } +} + +// Stored in this order: Zaaksysteem, Agenda, Mail. +const PLACEMENTS = [ + tile(1, 'Zaaksysteem', 0), + tile(2, 'Agenda', 1), + tile(3, 'Mail', 2), +] + +function mountSorted(sortBy, editMode = false) { + return mount(ContainerWidget, { + props: { + content: { title: 'Applicaties', sortBy, placements: PLACEMENTS }, + editMode, + }, + global: { + stubs: { + ContainerChild: { + props: ['placement'], + template: + '{{ placement.content.title }}', + }, + }, + }, + }) +} + +function order(wrapper) { + return wrapper + .findAll('.container-widget__child') + .map((item) => ({ + title: item.text(), + x: Number(item.attributes('gs-x')), + y: Number(item.attributes('gs-y')), + })) + .sort((a, b) => a.y - b.y || a.x - b.x) + .map((item) => item.title) +} + +describe('ContainerWidget tile sorting', () => { + beforeEach(() => { + globalThis.t = (_app, key) => key + localStorage.clear() + __resetTileUseForTest() + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('REQ-TSO-001: alphabetical shows Agenda, Mail, Zaaksysteem from left to right', () => { + expect(order(mountSorted('alphabetical'))).toEqual([ + 'Agenda', + 'Mail', + 'Zaaksysteem', + ]) + }) + + it('REQ-TSO-001: by hand keeps the stored order', () => { + expect(order(mountSorted('manual'))).toEqual([ + 'Zaaksysteem', + 'Agenda', + 'Mail', + ]) + }) + + it('REQ-TSO-001: edit mode shows the stored positions and nothing is written back', () => { + const wrapper = mountSorted('alphabetical', true) + expect(order(wrapper)).toEqual(['Zaaksysteem', 'Agenda', 'Mail']) + expect(mountSorted('alphabetical').emitted('update:content')).toBeUndefined() + }) + + it('REQ-TSO-001: the reflow keeps each tile size and wraps at four columns', () => { + const wide = [ + { ...tile(1, 'B', 0), gridWidth: 3 }, + { ...tile(2, 'A', 3), gridWidth: 2, gridHeight: 2 }, + ] + const wrapper = mount(ContainerWidget, { + props: { content: { sortBy: 'alphabetical', placements: wide } }, + global: { stubs: { ContainerChild: true } }, + }) + const items = wrapper.findAll('.container-widget__child') + const a = items.find((item) => item.attributes('gs-w') === '2') + const b = items.find((item) => item.attributes('gs-w') === '3') + expect([ + a.attributes('gs-x'), + a.attributes('gs-y'), + a.attributes('gs-h'), + ]).toEqual(['0', '0', '2']) + expect([b.attributes('gs-x'), b.attributes('gs-y')]).toEqual(['0', '2']) + }) + + it('REQ-TSO-002: a daily app moves to the front, and unused tiles fall back to alphabetical', () => { + recordLocalTileUse(1) + recordLocalTileUse(1) + recordLocalTileUse(1) + expect(order(mountSorted('most-used'))).toEqual([ + 'Zaaksysteem', + 'Agenda', + 'Mail', + ]) + + localStorage.clear() + __resetTileUseForTest() + recordLocalTileUse(3) + expect(order(mountSorted('most-used'))).toEqual([ + 'Mail', + 'Agenda', + 'Zaaksysteem', + ]) + }) + + it('REQ-TSO-002: clicking a tile in the container counts it in the browser only', async () => { + const axios = (await import('@nextcloud/axios')).default + const wrapper = mountSorted('most-used') + await wrapper.findAll('.container-widget__child')[2].trigger('click') + + const stored = JSON.parse(localStorage.getItem('launchpad.tileUse')) + expect(Object.keys(stored)).toHaveLength(1) + expect(Object.values(stored)[0].count).toBe(1) + expect(axios.post).not.toHaveBeenCalled() + }) + + it('REQ-TSO-002: last used puts the latest click first', () => { + vi.spyOn(Date, 'now').mockReturnValueOnce(1000).mockReturnValueOnce(2000) + recordLocalTileUse(2) + recordLocalTileUse(3) + expect(order(mountSorted('last-used'))).toEqual([ + 'Mail', + 'Agenda', + 'Zaaksysteem', + ]) + }) + + it('REQ-TSO-002: blocked storage shows the stored order without an error', () => { + vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => { + throw new Error('SecurityError') + }) + expect(order(mountSorted('most-used'))).toEqual([ + 'Zaaksysteem', + 'Agenda', + 'Mail', + ]) + }) + + it('REQ-TSO-003: forget my usage returns the container to alphabetical order', async () => { + recordLocalTileUse(1) + const wrapper = mountSorted('most-used') + expect(order(wrapper)).toEqual(['Zaaksysteem', 'Agenda', 'Mail']) + + const forget = wrapper.find('button.container-widget__forget') + expect(forget.text()).toBe('Forget my usage') + await forget.trigger('click') + + expect(order(wrapper)).toEqual(['Agenda', 'Mail', 'Zaaksysteem']) + expect(wrapper.find('button.container-widget__forget').exists()).toBe(false) + }) + + it('REQ-TSO-003: the forget button is not offered for alphabetical or by hand', () => { + recordLocalTileUse(1) + expect( + mountSorted('alphabetical') + .find('button.container-widget__forget') + .exists(), + ).toBe(false) + }) + + it('REQ-TSO-004: random order holds for the page load', async () => { + const first = order(mountSorted('random')) + const wrapper = mountSorted('random') + await wrapper.setProps({ + content: { sortBy: 'random', placements: [...PLACEMENTS] }, + }) + expect(order(wrapper)).toEqual(first) + expect([...first].sort()).toEqual(['Agenda', 'Mail', 'Zaaksysteem']) + }) + + it('announces the sort order to assistive technology', () => { + expect( + mountSorted('most-used') + .find('.launchpad-container-grid') + .attributes('aria-description'), + ).toBe('Sorted by most used') + expect( + mountSorted('manual') + .find('.launchpad-container-grid') + .attributes('aria-description'), + ).toBeUndefined() + }) +}) diff --git a/src/components/Widgets/Renderers/__tests__/ContainerWidgetForm.spec.js b/src/components/Widgets/Renderers/__tests__/ContainerWidgetForm.spec.js new file mode 100644 index 000000000..0511540fd --- /dev/null +++ b/src/components/Widgets/Renderers/__tests__/ContainerWidgetForm.spec.js @@ -0,0 +1,110 @@ +/** + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * Vitest unit tests for `ContainerWidgetForm.vue` (REQ-CONT-007 as modified + * by launcher-tile-sorting): the three communal fields plus exactly one + * "Sort tiles" select, and the emitted content carries `sortBy`. + */ + +import { mount } from '@vue/test-utils' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import ContainerWidgetForm from '../ContainerWidgetForm.vue' + +vi.mock('@conduction/nextcloud-vue', () => ({ + CnContainerWidgetForm: { + name: 'CnContainerWidgetForm', + emits: ['update:content'], + template: + '
', + }, +})) + +const NcSelectStub = { + name: 'NcSelect', + props: ['modelValue', 'options', 'inputLabel'], + emits: ['update:modelValue'], + template: + '
{{ modelValue }}
', +} + +describe('ContainerWidgetForm', () => { + beforeEach(() => { + globalThis.t = (_app, key) => key + }) + + function mountForm(props = {}) { + return mount(ContainerWidgetForm, { + props, + global: { stubs: { NcSelect: NcSelectStub } }, + }) + } + + it('renders the communal form plus exactly one Sort tiles select, defaulting to by hand', () => { + const wrapper = mountForm() + expect(wrapper.find('.communal').exists()).toBe(true) + const selects = wrapper.findAll('.nc-select') + expect(selects).toHaveLength(1) + expect(selects[0].attributes('data-label')).toBe('Sort tiles') + expect(selects[0].text()).toBe('manual') + const values = wrapper + .findComponent(NcSelectStub) + .props('options') + .map((option) => option.value) + expect(values).toEqual([ + 'manual', + 'alphabetical', + 'most-used', + 'last-used', + 'random', + ]) + }) + + it('emits the communal fields together with sortBy', async () => { + const wrapper = mountForm({ + editingWidget: { + content: { + title: 'Applicaties', + padding: 'small', + placements: [{ id: 1 }], + sortBy: 'most-used', + }, + }, + }) + expect(wrapper.find('.nc-select').text()).toBe('most-used') + + wrapper + .findComponent(NcSelectStub) + .vm.$emit('update:modelValue', 'alphabetical') + wrapper + .findComponent({ name: 'CnContainerWidgetForm' }) + .vm.$emit('update:content', { + placements: [{ id: 1 }], + backgroundColor: '#fff', + padding: 'small', + title: 'Apps', + }) + + const emitted = wrapper.emitted('update:content') + expect(emitted[0][0]).toMatchObject({ + title: 'Applicaties', + sortBy: 'alphabetical', + }) + expect(emitted[1][0]).toEqual({ + placements: [{ id: 1 }], + backgroundColor: '#fff', + padding: 'small', + title: 'Apps', + sortBy: 'alphabetical', + }) + expect(wrapper.vm.validate()).toEqual([]) + }) + + it('drops an unknown sort mode back to by hand', () => { + expect( + mountForm({ value: { sortBy: 'sideways' } }) + .find('.nc-select') + .text(), + ).toBe('manual') + }) +}) diff --git a/src/components/__tests__/RuntimeShellSearch.shortcuts.spec.js b/src/components/__tests__/RuntimeShellSearch.shortcuts.spec.js new file mode 100644 index 000000000..761c46021 --- /dev/null +++ b/src/components/__tests__/RuntimeShellSearch.shortcuts.spec.js @@ -0,0 +1,138 @@ +/** + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * Vitest unit tests for search shortcuts in `RuntimeShellSearch.vue` and + * `useTileSearch.js` (search-ai-prefix-shortcuts, REQ-SPX-002, REQ-SPX-003): + * a known prefix shows one result naming the site and opens it on Enter, + * an unknown prefix falls through to tile filtering, and `?` or `!` lists + * the shortcuts. + */ + +import { mount } from '@vue/test-utils' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import RuntimeShellSearch from '../RuntimeShellSearch.vue' +import { parsePrefix, shortcutUrl } from '../../composables/useTileSearch.js' + +const SHORTCUTS = [ + { + prefix: '!t', + name: 'TOPdesk', + urlTemplate: 'https://topdesk.gemeente.nl/tas/secure/search?q={query}', + }, + { + prefix: '!z', + name: 'Zaaksysteem', + urlTemplate: 'https://zaken.gemeente.nl/zoek?q={query}', + }, +] +const ITEMS = [ + { id: 'r1', label: '!x rapport' }, + { id: 'a1', label: 'Agenda' }, +] + +describe('search shortcuts', () => { + let wrapper + + beforeEach(() => { + globalThis.t = (_app, key) => key + }) + + afterEach(() => { + wrapper?.unmount() + }) + + function mountSearch() { + wrapper = mount(RuntimeShellSearch, { + attachTo: document.body, + props: { items: ITEMS, fallbackTarget: 'none', shortcuts: SHORTCUTS }, + }) + return wrapper + } + + async function type(value) { + const input = wrapper.find('input') + input.element.value = value + await input.trigger('input') + } + + it('parses the first word as a prefix, case-insensitively, and encodes the rest', () => { + expect(parsePrefix('!T printer 3e verdieping', SHORTCUTS)).toEqual({ + shortcut: SHORTCUTS[0], + rest: 'printer 3e verdieping', + }) + expect(parsePrefix('!x rapport', SHORTCUTS)).toBeNull() + expect(parsePrefix('printer !t', SHORTCUTS)).toBeNull() + expect(shortcutUrl(SHORTCUTS[0], 'printer 3e verdieping')).toBe( + 'https://topdesk.gemeente.nl/tas/secure/search?q=printer%203e%20verdieping', + ) + expect( + shortcutUrl({ urlTemplate: 'javascript:alert({query})' }, 'x'), + ).toBeNull() + }) + + it('REQ-SPX-002: the only result names the site and Enter opens it, without dimming tiles', async () => { + mountSearch() + await type('!t printer 3e verdieping') + + const options = wrapper.findAll('[data-test="quick-search-option"]') + expect(options).toHaveLength(1) + expect(options[0].text()).toBe('Search TOPdesk for printer 3e verdieping') + expect(wrapper.emitted('filter').at(-1)).toEqual([null]) + + await wrapper.find('input').trigger('keydown', { key: 'Enter' }) + expect(wrapper.emitted('fallback').at(-1)).toEqual([ + { + type: 'web-search', + url: 'https://topdesk.gemeente.nl/tas/secure/search?q=printer%203e%20verdieping', + }, + ]) + expect(wrapper.emitted('open')).toBeUndefined() + }) + + it('REQ-SPX-002: a prefix with nothing after it opens nothing', async () => { + mountSearch() + await type('!t') + await wrapper.find('input').trigger('keydown', { key: 'Enter' }) + expect(wrapper.emitted('fallback')).toBeUndefined() + }) + + it('REQ-SPX-002: an unknown prefix filters tiles as before', async () => { + mountSearch() + await type('!x rapport') + + const options = wrapper.findAll('[data-test="quick-search-option"]') + expect(options.map((option) => option.text())).toEqual(['!x rapport']) + expect(wrapper.emitted('filter').at(-1)).toEqual([['r1']]) + }) + + it('REQ-SPX-003: ? lists the shortcuts in a labelled list, and Enter types the prefix', async () => { + mountSearch() + await type('?') + + const list = wrapper.find('[role="listbox"]') + expect(list.attributes('aria-label')).toBe('Search shortcuts') + expect( + wrapper + .findAll('[data-test="quick-search-option"]') + .map((option) => option.text()), + ).toEqual(['!t TOPdesk', '!z Zaaksysteem']) + expect(wrapper.find('[data-test="quick-search-status"]').text()).toBe( + '!t TOPdesk, !z Zaaksysteem', + ) + + await wrapper.find('input').trigger('keydown', { key: 'Enter' }) + expect(wrapper.vm.search.state.query).toBe('!t ') + expect(wrapper.emitted('fallback')).toBeUndefined() + }) + + it('REQ-SPX-003: ! alone lists them too; without shortcuts ? is an ordinary query', async () => { + mountSearch() + await type('!') + expect(wrapper.findAll('[data-test="quick-search-option"]')).toHaveLength(2) + + await wrapper.setProps({ shortcuts: [] }) + await type('?') + expect(wrapper.find('[role="listbox"]').exists()).toBe(false) + }) +}) diff --git a/src/components/admin/AdminSettings.vue b/src/components/admin/AdminSettings.vue index 0214f247a..f3dfed270 100644 --- a/src/components/admin/AdminSettings.vue +++ b/src/components/admin/AdminSettings.vue @@ -99,6 +99,9 @@ + + + + + + + diff --git a/src/components/admin/tabs/__tests__/SearchShortcutsTab.spec.js b/src/components/admin/tabs/__tests__/SearchShortcutsTab.spec.js new file mode 100644 index 000000000..1dda0399d --- /dev/null +++ b/src/components/admin/tabs/__tests__/SearchShortcutsTab.spec.js @@ -0,0 +1,105 @@ +/** + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * Vitest unit tests for `SearchShortcutsTab.vue` (REQ-SPX-001): the list + * loads, a shortcut is added and saved, and the server's refusal of a plain + * http address is shown. + */ + +import axios from '@nextcloud/axios' +import { mount } from '@vue/test-utils' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import SearchShortcutsTab from '../SearchShortcutsTab.vue' + +vi.mock('@nextcloud/axios', () => ({ default: { get: vi.fn(), put: vi.fn() } })) +vi.mock('@nextcloud/router', async (importOriginal) => ({ + ...(await importOriginal()), + generateUrl: (path) => path, +})) + +function settle() { + return new Promise((resolve) => setTimeout(resolve, 0)) +} +const stubs = { + NcButton: { + props: ['disabled'], + emits: ['click'], + template: + '', + }, +} + +describe('SearchShortcutsTab', () => { + beforeEach(() => { + axios.get.mockReset() + axios.put.mockReset() + }) + + it('adds the TOPdesk shortcut and saves the list', async () => { + axios.get.mockResolvedValue({ data: { shortcuts: [] } }) + axios.put.mockImplementation((url, body) => + Promise.resolve({ data: { shortcuts: body.shortcuts } }), + ) + const wrapper = mount(SearchShortcutsTab, { global: { stubs } }) + await settle() + await settle() + + await wrapper + .findAll('button') + .find((button) => button.text() === 'Add shortcut') + .trigger('click') + const inputs = wrapper.findAll('tbody input') + await inputs[0].setValue('!t') + await inputs[1].setValue('TOPdesk') + await inputs[2].setValue( + 'https://topdesk.gemeente.nl/tas/secure/search?q={query}', + ) + await wrapper.find('form').trigger('submit') + await settle() + + expect(axios.put).toHaveBeenCalledWith( + '/apps/launchpad/api/admin/search-shortcuts', + { + shortcuts: [ + { + prefix: '!t', + name: 'TOPdesk', + urlTemplate: + 'https://topdesk.gemeente.nl/tas/secure/search?q={query}', + }, + ], + }, + ) + expect(wrapper.text()).toContain('Search shortcuts saved.') + }) + + it('shows the server refusal of a plain http address', async () => { + axios.get.mockResolvedValue({ + data: { + shortcuts: [ + { + prefix: '!i', + name: 'Intranet', + urlTemplate: 'http://intranet.gemeente.nl/zoek?q={query}', + }, + ], + }, + }) + axios.put.mockRejectedValue({ + response: { + data: { error: 'Use an https address that contains {query}: !i' }, + }, + }) + const wrapper = mount(SearchShortcutsTab, { global: { stubs } }) + await settle() + await settle() + + await wrapper.find('form').trigger('submit') + await settle() + + expect(wrapper.find('[role="alert"]').text()).toBe( + 'Use an https address that contains {query}: !i', + ) + }) +}) diff --git a/src/composables/useTileClickTracking.js b/src/composables/useTileClickTracking.js index 27b1798e8..269c6169d 100644 --- a/src/composables/useTileClickTracking.js +++ b/src/composables/useTileClickTracking.js @@ -123,3 +123,136 @@ export function __resetTileClickTrackingForTest() { cachedEnabled = null inflightConfigPromise = null } + +/** + * Browser storage key of the viewer's own tile use (launcher-tile-sorting). + * + * @type {string} + */ +const TILE_USE_KEY = 'launchpad.tileUse' + +/** + * Read the viewer's own tile use: `{ [placementId]: {count, lastUsedAt} }`. + * The counts live only in this browser and are never sent to the server + * (REQ-TSO-002). Corrupt data reads as no use; blocked storage reads as null. + * + * @return {Object|null} Use per placement id, or null when storage is unavailable. + * @spec openspec/specs/container-widget/spec.md + */ +export function readLocalTileUse() { + let raw + try { + raw = window.localStorage.getItem(TILE_USE_KEY) + } catch { + return null + } + try { + const parsed = JSON.parse(raw || '{}') + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) + ? parsed + : {} + } catch { + return {} + } +} + +/** + * Write the use map; a blocked storage is ignored. + * + * @param {object} use The use map. + * @return {boolean} True when stored. + * @spec openspec/specs/container-widget/spec.md + */ +function writeLocalTileUse(use) { + try { + window.localStorage.setItem(TILE_USE_KEY, JSON.stringify(use)) + return true + } catch { + return false + } +} + +/** + * Count one use of a tile in this browser (REQ-TSO-002). Never throws. + * + * @param {string|number} placementId The tile's placement id. + * @return {void} + * @spec openspec/specs/container-widget/spec.md + */ +export function recordLocalTileUse(placementId) { + if (!placementId && placementId !== 0) { + return + } + const use = readLocalTileUse() + if (use === null) { + return + } + const key = String(placementId) + const previous = use[key] || { count: 0, lastUsedAt: 0 } + use[key] = { count: (Number(previous.count) || 0) + 1, lastUsedAt: Date.now() } + writeLocalTileUse(use) +} + +/** + * Forget this browser's use of the given tiles (REQ-TSO-003). + * + * @param {Array} placementIds The tiles to forget. + * @return {void} + * @spec openspec/specs/container-widget/spec.md + */ +export function forgetLocalTileUse(placementIds) { + const use = readLocalTileUse() + if (use === null) { + return + } + placementIds.forEach((id) => { + delete use[String(id)] + }) + writeLocalTileUse(use) +} + +/** + * Test-only reset of the local tile use and the page-load shuffle. + * + * @spec exclude test-only harness hook; `src/components/Widgets/Renderers/__tests__/ContainerWidget.sort.spec.js` is its only importer. It clears the browser use map and the random ranks so each test starts cold. + * @return {void} + */ +export function __resetTileUseForTest() { + try { + window.localStorage.removeItem(TILE_USE_KEY) + } catch { + // blocked storage: nothing to clear + } + resetRandomRanks() +} + +/** + * Random rank per placement id, assigned once per page load (REQ-TSO-004). + * + * @type {Map} + */ +let randomRanks = new Map() + +/** + * The page-load random rank of a tile. + * + * @param {string} key The tile key. + * @return {number} A rank in [0, 1). + * @spec openspec/specs/container-widget/spec.md + */ +export function randomRankFor(key) { + if (!randomRanks.has(key)) { + randomRanks.set(key, Math.random()) + } + return randomRanks.get(key) +} + +/** + * Drop the page-load random ranks. + * + * @return {void} + * @spec openspec/specs/container-widget/spec.md + */ +function resetRandomRanks() { + randomRanks = new Map() +} diff --git a/src/composables/useTileSearch.js b/src/composables/useTileSearch.js index ede1885c8..aee01a3e6 100644 --- a/src/composables/useTileSearch.js +++ b/src/composables/useTileSearch.js @@ -291,6 +291,56 @@ export function isCtrlKFocusShortcut(event) { return Boolean(event.ctrlKey || event.metaKey) } +/** + * Split a query into a known search shortcut and the rest (REQ-SPX-002). + * The first word must equal a shortcut prefix, case-insensitively. + * + * @param {string} query The raw query. + * @param {Array<{prefix: string, name: string, urlTemplate: string}>} shortcuts The admin's shortcuts. + * @return {{shortcut: object, rest: string}|null} The match, or null. + * @spec openspec/specs/tile-quick-search/spec.md + */ +export function parsePrefix(query, shortcuts) { + const trimmed = String(query ?? '').trim() + if (!trimmed.startsWith('!') || !Array.isArray(shortcuts)) { + return null + } + const [first, ...rest] = trimmed.split(/\s+/) + const shortcut = shortcuts.find( + (entry) => String(entry?.prefix || '').toLowerCase() === first.toLowerCase(), + ) + return shortcut ? { shortcut, rest: rest.join(' ') } : null +} + +/** + * The address a shortcut opens for a query: its template with the + * URL-encoded query, only when the template is a valid https `{query}` + * address (REQ-SPX-002). + * + * @param {{urlTemplate: string}} shortcut The shortcut. + * @param {string} rest The query after the prefix. + * @return {string|null} The address, or null. + * @spec openspec/specs/tile-quick-search/spec.md + */ +export function shortcutUrl(shortcut, rest) { + if (!isValidFallbackTemplate(shortcut?.urlTemplate)) { + return null + } + return shortcut.urlTemplate.replace('{query}', encodeURIComponent(rest)) +} + +/** + * Whether a query asks for the shortcut list: `!` alone or `?` (REQ-SPX-003). + * + * @param {string} query The raw query. + * @return {boolean} True for the list. + * @spec openspec/specs/tile-quick-search/spec.md + */ +export function isShortcutListQuery(query) { + const trimmed = String(query ?? '').trim() + return trimmed === '!' || trimmed === '?' +} + /** * Factory — one instance per mounted search bar. Mirrors the * `useGridManager()` shape: a `reactive()` reactive `state` plus @@ -307,12 +357,24 @@ export function isCtrlKFocusShortcut(event) { * @param {() => string} [options.getFallbackTarget] returns the current * `quicksearch_fallback_target` value; consulted lazily so the caller * can change it without recreating the composable. + * @param {() => Array} [options.getShortcuts] returns the admin's + * search shortcuts (REQ-SPX-002); consulted on every recompute. * @return {object} the `{state, ...methods}` API — see inline method docs. * * @spec openspec/specs/tile-quick-search/spec.md */ export function useTileSearch(options = {}) { - const { onOpen, onFallback, getFallbackTarget } = options + const { onOpen, onFallback, getFallbackTarget, getShortcuts } = options + + /** + * The admin's shortcuts, or an empty list. + * + * @return {Array} Shortcuts. + */ + function shortcuts() { + const list = typeof getShortcuts === 'function' ? getShortcuts() : [] + return Array.isArray(list) ? list : [] + } const state = reactive({ query: '', @@ -337,6 +399,34 @@ export function useTileSearch(options = {}) { state.activeIndex = -1 return } + // REQ-SPX-003: `!` or `?` lists the shortcuts. + if (isShortcutListQuery(trimmed) && shortcuts().length > 0) { + state.results = shortcuts().map((shortcut) => ({ + id: `shortcut-list:${shortcut.prefix}`, + label: `${shortcut.prefix} ${shortcut.name}`, + kind: 'shortcut-list', + shortcut, + item: null, + })) + state.activeIndex = 0 + return + } + // REQ-SPX-002: a known prefix wins over tile ranking. + const prefixed = parsePrefix(trimmed, shortcuts()) + if (prefixed) { + state.results = [ + { + id: `shortcut:${prefixed.shortcut.prefix}`, + label: prefixed.shortcut.name, + kind: 'shortcut', + shortcut: prefixed.shortcut, + rest: prefixed.rest, + item: null, + }, + ] + state.activeIndex = 0 + return + } state.results = rankItems(trimmed, items) state.activeIndex = state.results.length > 0 ? 0 : -1 } @@ -412,6 +502,24 @@ export function useTileSearch(options = {}) { */ function pressEnter() { const result = activeResult() + // REQ-SPX-003: Enter on a listed shortcut types its prefix. + if (result && result.kind === 'shortcut-list') { + setQuery(`${result.shortcut.prefix} `) + return { type: 'shortcut-list', shortcut: result.shortcut } + } + // REQ-SPX-002: Enter on a shortcut opens its site in a new tab. + if (result && result.kind === 'shortcut') { + const url = result.rest + ? shortcutUrl(result.shortcut, result.rest) + : null + const action = url + ? { type: 'web-search', url } + : { type: FALLBACK_TARGET_NONE } + if (url && typeof onFallback === 'function') { + onFallback(action) + } + return action + } if (result) { if (typeof onOpen === 'function') { onOpen(result.item) diff --git a/src/constants/widgetRegistry.js b/src/constants/widgetRegistry.js index ae0f9c637..eed49b4a9 100644 --- a/src/constants/widgetRegistry.js +++ b/src/constants/widgetRegistry.js @@ -70,6 +70,7 @@ import ChartHost from '../components/Widgets/Renderers/ChartHost.vue' import ClockWidget from '../components/Widgets/Renderers/ClockWidget.vue' import ClockWidgetForm from '../components/Widgets/Renderers/ClockWidgetForm.vue' import ContainerWidget from '../components/Widgets/Renderers/ContainerWidget.vue' +import ContainerWidgetForm from '../components/Widgets/Renderers/ContainerWidgetForm.vue' // `iframe` — embeds an admin-allow-listed external URL in a sandboxed // frame, with a client-side graceful-degradation fallback for targets that // refuse framing (REQ-IFRAME-001..004, openspec/specs/iframe-embed-widget). @@ -129,6 +130,8 @@ const RENDERER_OVERRIDES = { */ const FORM_OVERRIDES = { calendar: CalendarForm, + // launcher-tile-sorting: the communal fields plus "Sort tiles". + container: ContainerWidgetForm, people: PeopleForm, 'spend-analytics': SpendAnalyticsForm, 'nc-widget': NcDashboardForm, diff --git a/src/utils/loadInitialState.js b/src/utils/loadInitialState.js index 27d658e42..b0257d5ee 100644 --- a/src/utils/loadInitialState.js +++ b/src/utils/loadInitialState.js @@ -63,6 +63,10 @@ const PAGE_KEYS = { // PHP side yet simply omits it, and 'none' is the safe default // (no navigation on no-match Enter). quicksearchFallbackTarget: 'none', + // search-ai-prefix-shortcuts REQ-SPX-002: the admin's search + // shortcuts `{prefix, name, urlTemplate}`. Optional key; an empty + // list keeps every search box as it was. + searchShortcuts: [], // runtime-shell REQ-SHELL-009: the dashboard view without the left // navigation panel. Optional key, same rationale as `deepLinkPath`: // a server without the setter keeps the panel. diff --git a/src/utils/sortContainerTiles.js b/src/utils/sortContainerTiles.js new file mode 100644 index 000000000..4e5170456 --- /dev/null +++ b/src/utils/sortContainerTiles.js @@ -0,0 +1,116 @@ +/** + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * View-time ordering and reflow of the tiles in a container + * (launcher-tile-sorting, REQ-TSO-001..004). Pure functions: the caller + * supplies the viewer's own use map and the page-load random ranks, and + * gets back copies with new inner-grid positions. Nothing is written back. + */ + +export const SORT_MODES = Object.freeze([ + 'manual', + 'alphabetical', + 'most-used', + 'last-used', + 'random', +]) + +/** + * The display title of a child placement, for the alphabetical order. + * + * @param {object} child The child placement. + * @return {string} Its title, or ''. + * @spec openspec/specs/container-widget/spec.md + */ +export function childTitle(child) { + const content = child?.content || {} + const candidates = [ + content.title, + content.label, + content.text, + child?.tileTitle, + child?.customTitle, + child?.title, + ] + const found = candidates.find( + (value) => typeof value === 'string' && value.trim() !== '', + ) + return found ? found.trim() : '' +} + +/** + * Order the children for a sort mode. Most and last used fall back to + * alphabetical for equal values, so unused tiles sort after used ones. + * + * @param {Array} children Child placements in stored order. + * @param {string} sortBy One of SORT_MODES. + * @param {object} options Sort inputs. + * @param {object} options.use The viewer's use per key. + * @param {function(object): string} options.keyOf The key of a child. + * @param {function(string): number} options.rankOf The page-load random rank of a key. + * @param {Intl.Collator} options.collator Locale collator for titles. + * @return {Array} A new, ordered array. + * @spec openspec/specs/container-widget/spec.md + */ +export function orderChildren( + children, + sortBy, + { use = {}, keyOf, rankOf, collator }, +) { + const list = [...children] + const byTitle = (a, b) => collator.compare(childTitle(a), childTitle(b)) + const stat = (child, field) => Number(use[keyOf(child)]?.[field]) || 0 + + switch (sortBy) { + case 'alphabetical': + return list.sort(byTitle) + case 'most-used': + return list.sort( + (a, b) => stat(b, 'count') - stat(a, 'count') || byTitle(a, b), + ) + case 'last-used': + return list.sort( + (a, b) => + stat(b, 'lastUsedAt') - stat(a, 'lastUsedAt') || byTitle(a, b), + ) + case 'random': + return list.sort((a, b) => rankOf(keyOf(a)) - rankOf(keyOf(b))) + default: + return list + } +} + +/** + * Reflow ordered children row by row into a grid of `columns`, keeping + * each child's width and height. A row is as tall as its tallest child. + * + * @param {Array} ordered Ordered child placements. + * @param {number} columns Inner-grid column count. + * @return {Array} Copies with new `gridX` and `gridY`. + * @spec openspec/specs/container-widget/spec.md + */ +export function reflowChildren(ordered, columns) { + let x = 0 + let y = 0 + let rowHeight = 0 + return ordered.map((child) => { + const width = Math.min(Math.max(Number(child.gridWidth) || 2, 1), columns) + const height = Math.max(Number(child.gridHeight) || 2, 1) + if (x + width > columns) { + x = 0 + y += rowHeight + rowHeight = 0 + } + const placed = { + ...child, + gridX: x, + gridY: y, + gridWidth: width, + gridHeight: height, + } + x += width + rowHeight = Math.max(rowHeight, height) + return placed + }) +} diff --git a/tests/Unit/Controller/PageControllerBridgedWidgetsTest.php b/tests/Unit/Controller/PageControllerBridgedWidgetsTest.php index 129bb9211..de29ee609 100644 --- a/tests/Unit/Controller/PageControllerBridgedWidgetsTest.php +++ b/tests/Unit/Controller/PageControllerBridgedWidgetsTest.php @@ -64,6 +64,7 @@ private function makeController(WidgetService $widgetService): PageController { treeService: $this->createMock(DashboardTreeService::class), logger: $this->createMock(LoggerInterface::class), adminSettingsService: $this->createMock(AdminSettingsService::class), + searchShortcuts: $this->createMock(\OCA\LaunchPad\Service\SearchShortcutService::class), ); }//end makeController() diff --git a/tests/Unit/Controller/SearchShortcutsControllerTest.php b/tests/Unit/Controller/SearchShortcutsControllerTest.php new file mode 100644 index 000000000..506f3c2e8 --- /dev/null +++ b/tests/Unit/Controller/SearchShortcutsControllerTest.php @@ -0,0 +1,131 @@ + + * @copyright 2026 Conduction b.v. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + */ + +declare(strict_types=1); + +namespace Unit\Controller; + +use OCA\LaunchPad\Controller\SearchShortcutsController; +use OCA\LaunchPad\Db\AdminSetting; +use OCA\LaunchPad\Db\AdminSettingMapper; +use OCA\LaunchPad\Service\ActionAuthService; +use OCA\LaunchPad\Service\AdminSettingsService; +use OCA\LaunchPad\Service\SearchShortcutService; +use OCP\AppFramework\Http; +use OCP\AppFramework\OCS\OCSForbiddenException; +use OCP\IGroupManager; +use OCP\IRequest; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\TestCase; + +/** + * Tests for SearchShortcutsController. + */ +class SearchShortcutsControllerTest extends TestCase { + /** + * Stored admin settings. + * + * @var array + */ + private array $settings = []; + + /** + * A controller for a caller. + * + * @param string|null $uid The caller, or null when signed out. + * @param boolean $admin Whether the caller is an administrator. + * @param boolean $allowed Whether the action matrix lets them through. + * + * @return SearchShortcutsController + */ + private function controller(?string $uid, bool $admin = true, bool $allowed = true): SearchShortcutsController { + $mapper = $this->createMock(AdminSettingMapper::class); + $mapper->method('getValue')->willReturnCallback(fn (string $key, mixed $default = null): mixed => $this->settings[$key] ?? $default); + $mapper->method('setSetting')->willReturnCallback(function (string $key, mixed $value): AdminSetting { + $this->settings[$key] = $value; + return new AdminSetting(); + }); + $service = new SearchShortcutService(settingMapper: $mapper, adminSettings: new AdminSettingsService(settingMapper: $mapper)); + + $session = $this->createMock(IUserSession::class); + $user = null; + if ($uid !== null) { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn($uid); + } + + $session->method('getUser')->willReturn($user); + $groups = $this->createMock(IGroupManager::class); + $groups->method('isAdmin')->willReturn($admin); + $auth = $this->createMock(ActionAuthService::class); + if ($allowed === false) { + $auth->method('requireAction')->willThrowException(new OCSForbiddenException('no')); + } + + return new SearchShortcutsController( + request: $this->createMock(IRequest::class), + shortcuts: $service, + actionAuth: $auth, + userSession: $session, + groupManager: $groups, + ); + }//end controller() + + /** + * An administrator stores a list and reads it back. + * + * @return void + */ + public function testAnAdministratorSavesAndReadsTheShortcuts(): void { + $saved = $this->controller(uid: 'admin')->save(shortcuts: [['prefix' => '!T', 'name' => 'TOPdesk', 'urlTemplate' => 'https://topdesk.example.nl/search?q={query}']]); + $this->assertSame(Http::STATUS_OK, $saved->getStatus()); + + $read = $this->controller(uid: 'admin')->index()->getData()['shortcuts']; + $this->assertSame('!t', $read[0]['prefix']); + }//end testAnAdministratorSavesAndReadsTheShortcuts() + + /** + * An invalid list is 400 and stores nothing. + * + * @return void + */ + public function testAnInvalidListIs400(): void { + $response = $this->controller(uid: 'admin')->save(shortcuts: [['prefix' => 't', 'name' => 'T', 'urlTemplate' => 'https://x.example/?q={query}']]); + $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus()); + $this->assertArrayHasKey('error', $response->getData()); + $this->assertSame([], $this->settings); + }//end testAnInvalidListIs400() + + /** + * Signed out is 401, a non-admin 403, a refused action 403; nothing is stored. + * + * @return void + */ + public function testOnlyAnAllowedAdministratorGetsIn(): void { + $list = [['prefix' => '!t', 'name' => 'T', 'urlTemplate' => 'https://x.example/?q={query}']]; + foreach ([[null, true, true, Http::STATUS_UNAUTHORIZED], ['sanne', false, true, Http::STATUS_FORBIDDEN], ['admin', true, false, Http::STATUS_FORBIDDEN]] as [$uid, $admin, $allowed, $status]) { + $this->assertSame($status, $this->controller(uid: $uid, admin: $admin, allowed: $allowed)->index()->getStatus()); + $this->assertSame($status, $this->controller(uid: $uid, admin: $admin, allowed: $allowed)->save(shortcuts: $list)->getStatus()); + } + + $this->assertSame([], $this->settings, 'a refused write stores nothing'); + }//end testOnlyAnAllowedAdministratorGetsIn() +}//end class diff --git a/tests/Unit/Service/ActionAuthBaselineTest.php b/tests/Unit/Service/ActionAuthBaselineTest.php index 6dc033c92..83f46704a 100644 --- a/tests/Unit/Service/ActionAuthBaselineTest.php +++ b/tests/Unit/Service/ActionAuthBaselineTest.php @@ -84,6 +84,8 @@ class ActionAuthBaselineTest extends TestCase { 'metadata-admin.update-field', 'metadata-admin.delete-field', 'people-widget.get-users', + 'search-shortcut.list', + 'search-shortcut.save', 'profile-fields.get-definitions', 'profile-fields.save-definitions', 'rule.get-rules', diff --git a/tests/Unit/Service/InitialStateBuilderTest.php b/tests/Unit/Service/InitialStateBuilderTest.php index b021e2bd1..719b74c70 100644 --- a/tests/Unit/Service/InitialStateBuilderTest.php +++ b/tests/Unit/Service/InitialStateBuilderTest.php @@ -29,6 +29,34 @@ use PHPUnit\Framework\TestCase; class InitialStateBuilderTest extends TestCase { + /** + * REQ-SPX-002: the search shortcuts reach the workspace page. + * + * @return void + */ + public function testSearchShortcutsReachTheWorkspace(): void { + $sink = []; + $state = $this->makeRecordingState($sink); + $shortcuts = [['prefix' => '!t', 'name' => 'TOPdesk', 'urlTemplate' => 'https://topdesk.example.nl/?q={query}']]; + + (new InitialStateBuilder(initialState: $state, page: Page::WORKSPACE)) + ->setWidgets([]) + ->setLayout([]) + ->setPrimaryGroup('g') + ->setPrimaryGroupName('G') + ->setIsAdmin(false) + ->setActiveDashboardId('') + ->setDashboardSource('group') + ->setGroupDashboards([]) + ->setUserDashboards([]) + ->setAllowUserDashboards(false) + ->setAllowedWidgets(null) + ->setSearchShortcuts($shortcuts) + ->apply(); + + $this->assertSame($shortcuts, $sink['searchShortcuts']); + }//end testSearchShortcutsReachTheWorkspace() + /** * In-memory IInitialState stub that records every push. * diff --git a/tests/Unit/Service/SearchShortcutServiceTest.php b/tests/Unit/Service/SearchShortcutServiceTest.php new file mode 100644 index 000000000..f3dae7220 --- /dev/null +++ b/tests/Unit/Service/SearchShortcutServiceTest.php @@ -0,0 +1,124 @@ + + * @copyright 2026 Conduction b.v. + * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + */ + +declare(strict_types=1); + +namespace Unit\Service; + +use InvalidArgumentException; +use OCA\LaunchPad\Db\AdminSetting; +use OCA\LaunchPad\Db\AdminSettingMapper; +use OCA\LaunchPad\Service\AdminSettingsService; +use OCA\LaunchPad\Service\SearchShortcutService; +use PHPUnit\Framework\TestCase; + +class SearchShortcutServiceTest extends TestCase { + /** + * Stored admin settings. + * + * @var array + */ + private array $settings = []; + + private SearchShortcutService $service; + + protected function setUp(): void { + parent::setUp(); + + $mapper = $this->createMock(AdminSettingMapper::class); + $mapper->method('getValue')->willReturnCallback( + fn (string $key, mixed $default = null): mixed => $this->settings[$key] ?? $default + ); + $mapper->method('setSetting')->willReturnCallback( + function (string $key, mixed $value): AdminSetting { + $this->settings[$key] = $value; + return new AdminSetting(); + } + ); + + // The real template check, the one the quick-search fallback uses. + $adminSettings = new AdminSettingsService(settingMapper: $mapper); + + $this->service = new SearchShortcutService(settingMapper: $mapper, adminSettings: $adminSettings); + }//end setUp() + + public function testAValidListIsStoredWithLowerCasePrefixes(): void { + $saved = $this->service->saveShortcuts( + raw: [ + ['prefix' => '!T', 'name' => ' TOPdesk ', 'urlTemplate' => 'https://topdesk.example.nl/search?q={query}'], + ['prefix' => '!wiki', 'name' => 'Wikipedia', 'urlTemplate' => 'https://nl.wikipedia.org/w/index.php?search={query}'], + ] + ); + + $this->assertSame('!t', $saved[0]['prefix']); + $this->assertSame('TOPdesk', $saved[0]['name']); + $this->assertSame($saved, $this->service->getShortcuts()); + $this->assertSame($saved, $this->settings['search_shortcuts']); + }//end testAValidListIsStoredWithLowerCasePrefixes() + + /** + * @return array}> + */ + public static function invalidEntries(): array { + $template = 'https://example.nl/?q={query}'; + return [ + 'no bang' => [['prefix' => 't', 'name' => 'T', 'urlTemplate' => $template]], + 'too long' => [['prefix' => '!abcdefghijk', 'name' => 'T', 'urlTemplate' => $template]], + 'space in prefix' => [['prefix' => '!a b', 'name' => 'T', 'urlTemplate' => $template]], + 'no name' => [['prefix' => '!t', 'name' => ' ', 'urlTemplate' => $template]], + 'http' => [['prefix' => '!t', 'name' => 'T', 'urlTemplate' => 'http://example.nl/?q={query}']], + 'no placeholder' => [['prefix' => '!t', 'name' => 'T', 'urlTemplate' => 'https://example.nl/']], + 'javascript' => [['prefix' => '!t', 'name' => 'T', 'urlTemplate' => 'javascript:alert({query})']], + ]; + }//end invalidEntries() + + /** + * @dataProvider invalidEntries + */ + public function testAnInvalidEntryIsRefused(array $entry): void { + $this->expectException(InvalidArgumentException::class); + $this->service->saveShortcuts(raw: [$entry]); + }//end testAnInvalidEntryIsRefused() + + public function testPrefixesAreUniqueRegardlessOfCase(): void { + $this->expectException(InvalidArgumentException::class); + $this->service->saveShortcuts( + raw: [ + ['prefix' => '!t', 'name' => 'A', 'urlTemplate' => 'https://a.nl/?q={query}'], + ['prefix' => '!T', 'name' => 'B', 'urlTemplate' => 'https://b.nl/?q={query}'], + ] + ); + }//end testPrefixesAreUniqueRegardlessOfCase() + + public function testAtMostThirtyShortcuts(): void { + $raw = []; + for ($i = 0; $i < 31; $i++) { + $raw[] = ['prefix' => '!s' . $i, 'name' => 'S' . $i, 'urlTemplate' => 'https://s.nl/?q={query}']; + } + + $this->expectException(InvalidArgumentException::class); + $this->service->saveShortcuts(raw: $raw); + }//end testAtMostThirtyShortcuts() + + public function testAStoredListThatNoLongerValidatesReadsAsEmpty(): void { + $this->settings['search_shortcuts'] = [['prefix' => '!t', 'name' => 'T', 'urlTemplate' => 'http://insecure.nl/?q={query}']]; + + $this->assertSame([], $this->service->getShortcuts()); + }//end testAStoredListThatNoLongerValidatesReadsAsEmpty() +}//end class