Merge branch 'mealie-next' into fix/meal-plan-settings-offscreen

This commit is contained in:
boc-the-git 2023-12-11 12:33:41 +11:00 committed by GitHub
commit 36b35caffb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 333 additions and 323 deletions

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Sleutelwoord", "keyword": "Sleutelwoord",
"link-copied": "Skakel gekopieer", "link-copied": "Skakel gekopieer",
"loading": "Loading",
"loading-events": "Besig om gebeurtenisse te laai", "loading-events": "Besig om gebeurtenisse te laai",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "كلمة مفتاحية", "keyword": "كلمة مفتاحية",
"link-copied": "تمّ نسْخ الرّابط", "link-copied": "تمّ نسْخ الرّابط",
"loading": "Loading",
"loading-events": "جاري تحميل الأحداث", "loading-events": "جاري تحميل الأحداث",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Ключова дума", "keyword": "Ключова дума",
"link-copied": "Линкът е копиран", "link-copied": "Линкът е копиран",
"loading": "Loading",
"loading-events": "Зареждане на събития", "loading-events": "Зареждане на събития",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Paraula clau", "keyword": "Paraula clau",
"link-copied": "S'ha copiat l'enllaç", "link-copied": "S'ha copiat l'enllaç",
"loading": "Loading",
"loading-events": "Carregant esdeveniments", "loading-events": "Carregant esdeveniments",
"loading-recipe": "Carregant la recepta...", "loading-recipe": "Carregant la recepta...",
"loading-ocr-data": "Carregant les dades OCR...", "loading-ocr-data": "Carregant les dades OCR...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Klíčové slovo", "keyword": "Klíčové slovo",
"link-copied": "Odkaz zkopírován", "link-copied": "Odkaz zkopírován",
"loading": "Loading",
"loading-events": "Načítání událostí", "loading-events": "Načítání událostí",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Nøgleord", "keyword": "Nøgleord",
"link-copied": "Link kopieret", "link-copied": "Link kopieret",
"loading": "Indlæser",
"loading-events": "Indlæser hændelser", "loading-events": "Indlæser hændelser",
"loading-recipe": "Indlæser opskrift...", "loading-recipe": "Indlæser opskrift...",
"loading-ocr-data": "Indlæser OCR data...", "loading-ocr-data": "Indlæser OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Schlüsselwort", "keyword": "Schlüsselwort",
"link-copied": "Link kopiert", "link-copied": "Link kopiert",
"loading": "Wird geladen...",
"loading-events": "Ereignisse werden geladen", "loading-events": "Ereignisse werden geladen",
"loading-recipe": "Lade Rezept...", "loading-recipe": "Lade Rezept...",
"loading-ocr-data": "Lade OCR-Daten...", "loading-ocr-data": "Lade OCR-Daten...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Λέξη-κλειδί", "keyword": "Λέξη-κλειδί",
"link-copied": "Ο Σύνδεσμος Αντιγράφηκε", "link-copied": "Ο Σύνδεσμος Αντιγράφηκε",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Keyword", "keyword": "Keyword",
"link-copied": "Link Copied", "link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Keyword", "keyword": "Keyword",
"link-copied": "Link Copied", "link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Etiqueta", "keyword": "Etiqueta",
"link-copied": "Enlace copiado", "link-copied": "Enlace copiado",
"loading": "Loading",
"loading-events": "Cargando Eventos", "loading-events": "Cargando Eventos",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Hakusana", "keyword": "Hakusana",
"link-copied": "Linkki kopioitu", "link-copied": "Linkki kopioitu",
"loading": "Loading",
"loading-events": "Ladataan tapahtumia", "loading-events": "Ladataan tapahtumia",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Mot-clé", "keyword": "Mot-clé",
"link-copied": "Lien copié", "link-copied": "Lien copié",
"loading": "Chargement",
"loading-events": "Chargement des événements", "loading-events": "Chargement des événements",
"loading-recipe": "Chargement de la recette...", "loading-recipe": "Chargement de la recette...",
"loading-ocr-data": "Chargement des données OCR...", "loading-ocr-data": "Chargement des données OCR...",
@ -875,7 +876,7 @@
"user-management": "Gestion des utilisateurs", "user-management": "Gestion des utilisateurs",
"reset-locked-users": "Réinitialiser les utilisateurs verrouillés", "reset-locked-users": "Réinitialiser les utilisateurs verrouillés",
"admin-user-creation": "Création d'un utilisateur admin", "admin-user-creation": "Création d'un utilisateur admin",
"admin-user-management": "Admin User Management", "admin-user-management": "Administration des utilisateurs",
"user-details": "Détails de l'utilisateur", "user-details": "Détails de l'utilisateur",
"user-name": "Nom d'utilisateur", "user-name": "Nom d'utilisateur",
"authentication-method": "Méthode d'authentification", "authentication-method": "Méthode d'authentification",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Mot-clé", "keyword": "Mot-clé",
"link-copied": "Lien copié", "link-copied": "Lien copié",
"loading": "Chargement",
"loading-events": "Chargement des événements", "loading-events": "Chargement des événements",
"loading-recipe": "Chargement de la recette...", "loading-recipe": "Chargement de la recette...",
"loading-ocr-data": "Chargement des données OCR...", "loading-ocr-data": "Chargement des données OCR...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Keyword", "keyword": "Keyword",
"link-copied": "Link Copied", "link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "מילת מפתח", "keyword": "מילת מפתח",
"link-copied": "קישור הועתק", "link-copied": "קישור הועתק",
"loading": "Loading",
"loading-events": "טוען", "loading-events": "טוען",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Ključna riječ", "keyword": "Ključna riječ",
"link-copied": "Poveznica kopirana", "link-copied": "Poveznica kopirana",
"loading": "Loading",
"loading-events": "Učitavanje događaja", "loading-events": "Učitavanje događaja",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -77,7 +77,7 @@
"tag-events": "Címke események", "tag-events": "Címke események",
"category-events": "Kategória események", "category-events": "Kategória események",
"when-a-new-user-joins-your-group": "Amikor egy új felhasználó csatlakozik a csoportodba", "when-a-new-user-joins-your-group": "Amikor egy új felhasználó csatlakozik a csoportodba",
"recipe-events": "Recipe Events" "recipe-events": "Recept esemény"
}, },
"general": { "general": {
"cancel": "Mégsem", "cancel": "Mégsem",
@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Kulcsszó", "keyword": "Kulcsszó",
"link-copied": "Hivatkozás másolva", "link-copied": "Hivatkozás másolva",
"loading": "Loading",
"loading-events": "Események betöltése", "loading-events": "Események betöltése",
"loading-recipe": "Recept betöltése...", "loading-recipe": "Recept betöltése...",
"loading-ocr-data": "OCR adatok betöltése...", "loading-ocr-data": "OCR adatok betöltése...",
@ -590,7 +591,7 @@
"import-summary": "Import összefoglaló", "import-summary": "Import összefoglaló",
"partial-backup": "Részleges biztonsági mentés", "partial-backup": "Részleges biztonsági mentés",
"unable-to-delete-backup": "Nem lehetett létrehozni a biztonsági mentést.", "unable-to-delete-backup": "Nem lehetett létrehozni a biztonsági mentést.",
"experimental-description": "Backups a total snapshots of the database and data directory of the site. This includes all data and cannot be set to exclude subsets of data. You can think off this as a snapshot of Mealie at a specific time. Currently, {not-crossed-version} (data migrations are not done automatically). These serve as a database agnostic way to export and import data or backup the site to an external location.", "experimental-description": "A biztonsági mentések az oldal adatbázisának és adatkönyvtárának teljes pillanatfelvételei. Ez az összes adatot tartalmazza, és nem lehet beállítani, hogy az adatok részhalmazait kizárja. Ezt úgy is elképzelheti, mint a Mealie egy adott időpontban készült pillanatfelvételét. Ezek adatbázis-független módon szolgálnak az adatok exportálására és importálására, vagy a webhely külső helyre történő mentésére.",
"backup-restore": "Biztonsági Mentés/Visszaállítás", "backup-restore": "Biztonsági Mentés/Visszaállítás",
"back-restore-description": "A biztonsági mentés visszaállítása felülírja az adatbázisban és az adatkönyvtárban lévő összes aktuális adatot, és a biztonsági mentés tartalmával helyettesíti azokat. {cannot-be-undone} Ha a visszaállítás sikeres, akkor a rendszer kilépteti Önt.", "back-restore-description": "A biztonsági mentés visszaállítása felülírja az adatbázisban és az adatkönyvtárban lévő összes aktuális adatot, és a biztonsági mentés tartalmával helyettesíti azokat. {cannot-be-undone} Ha a visszaállítás sikeres, akkor a rendszer kilépteti Önt.",
"cannot-be-undone": "Ezt a műveletet visszavonható - óvatosan használja.", "cannot-be-undone": "Ezt a műveletet visszavonható - óvatosan használja.",
@ -1056,8 +1057,8 @@
"click": "Kattintson bármelyik mezőre a jobb oldalon, majd kattintson vissza a kép feletti téglalapra.", "click": "Kattintson bármelyik mezőre a jobb oldalon, majd kattintson vissza a kép feletti téglalapra.",
"result": "A kiválasztott szöveg a korábban kiválasztott mezőben jelenik meg." "result": "A kiválasztott szöveg a korábban kiválasztott mezőben jelenik meg."
}, },
"pan-and-zoom-mode": "Pan and Zoom Mode", "pan-and-zoom-mode": "Pásztázás és nagyítás mód",
"pan-and-zoom-desc": "Select pan and zoom by clicking the icon. This mode allows to zoom inside the image and move around to make using big images easier.", "pan-and-zoom-desc": "Válassza ki a pásztázást és a nagyítást az ikonra kattintva. Ez a mód lehetővé teszi a kép nagyítását és mozgását a nagy képek használatának megkönnyítése érdekében.",
"split-text-mode": "Szöveg felosztási módok", "split-text-mode": "Szöveg felosztási módok",
"split-modes": { "split-modes": {
"line-mode": "Vonal mód (alapértelmezett)", "line-mode": "Vonal mód (alapértelmezett)",
@ -1113,7 +1114,7 @@
"show-individual-confidence": "", "show-individual-confidence": "",
"ingredient-text": "Hozzávaló szöveg", "ingredient-text": "Hozzávaló szöveg",
"average-confident": "{0} Confident", "average-confident": "{0} Confident",
"try-an-example": "Próbáld ki", "try-an-example": "Próbáljon ki egy példát",
"parser": "Szintaxis elemző", "parser": "Szintaxis elemző",
"background-tasks": "Háttér folyamatok", "background-tasks": "Háttér folyamatok",
"background-tasks-description": "Itt megtekintheti az összes futó háttérfeladatot és azok állapotát", "background-tasks-description": "Itt megtekintheti az összes futó háttérfeladatot és azok állapotát",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Parola chiave", "keyword": "Parola chiave",
"link-copied": "Link Copiato", "link-copied": "Link Copiato",
"loading": "Loading",
"loading-events": "Caricamento eventi", "loading-events": "Caricamento eventi",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "キーワード", "keyword": "キーワード",
"link-copied": "リンクをコピーしました。", "link-copied": "リンクをコピーしました。",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "키워드", "keyword": "키워드",
"link-copied": "링크 복사됨", "link-copied": "링크 복사됨",
"loading": "Loading",
"loading-events": "이벤트를 불러오는 중", "loading-events": "이벤트를 불러오는 중",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Raktažodis", "keyword": "Raktažodis",
"link-copied": "Nuoroda nukopijuota", "link-copied": "Nuoroda nukopijuota",
"loading": "Loading",
"loading-events": "Užkrovimo įvykiai", "loading-events": "Užkrovimo įvykiai",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Keyword", "keyword": "Keyword",
"link-copied": "Link Copied", "link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Trefwoord", "keyword": "Trefwoord",
"link-copied": "Link Gekopieerd", "link-copied": "Link Gekopieerd",
"loading": "Bezig met laden",
"loading-events": "Gebeurtenis laden", "loading-events": "Gebeurtenis laden",
"loading-recipe": "Recepten ophalen...", "loading-recipe": "Recepten ophalen...",
"loading-ocr-data": "OCR gegevens laden...", "loading-ocr-data": "OCR gegevens laden...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Nøkkelord", "keyword": "Nøkkelord",
"link-copied": "Lenke kopiert", "link-copied": "Lenke kopiert",
"loading": "Loading",
"loading-events": "Laster hendelser", "loading-events": "Laster hendelser",
"loading-recipe": "Laster oppskrift...", "loading-recipe": "Laster oppskrift...",
"loading-ocr-data": "Laster OCR data...", "loading-ocr-data": "Laster OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Słowo kluczowe", "keyword": "Słowo kluczowe",
"link-copied": "Odnośnik skopiowany", "link-copied": "Odnośnik skopiowany",
"loading": "Loading",
"loading-events": "Ładowanie wydarzeń", "loading-events": "Ładowanie wydarzeń",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Palavra chave", "keyword": "Palavra chave",
"link-copied": "Link Copiado", "link-copied": "Link Copiado",
"loading": "Loading",
"loading-events": "Carregando eventos", "loading-events": "Carregando eventos",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Palavra-chave", "keyword": "Palavra-chave",
"link-copied": "Ligação copiada", "link-copied": "Ligação copiada",
"loading": "Loading",
"loading-events": "A carregar Eventos", "loading-events": "A carregar Eventos",
"loading-recipe": "A carregar receita...", "loading-recipe": "A carregar receita...",
"loading-ocr-data": "A carregar dados OCR...", "loading-ocr-data": "A carregar dados OCR...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Cuvânt cheie", "keyword": "Cuvânt cheie",
"link-copied": "Link copiat", "link-copied": "Link copiat",
"loading": "Loading",
"loading-events": "Se încarcă evenimentele", "loading-events": "Se încarcă evenimentele",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Ключевое слово", "keyword": "Ключевое слово",
"link-copied": "Ссылка скопирована", "link-copied": "Ссылка скопирована",
"loading": "Loading",
"loading-events": "Загрузка событий", "loading-events": "Загрузка событий",
"loading-recipe": "Загрузка рецепта...", "loading-recipe": "Загрузка рецепта...",
"loading-ocr-data": "Загрузка данных OCR...", "loading-ocr-data": "Загрузка данных OCR...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Kľučové slovo", "keyword": "Kľučové slovo",
"link-copied": "Odkaz bol skopírovaný", "link-copied": "Odkaz bol skopírovaný",
"loading": "Loading",
"loading-events": "Načítanie udalostí", "loading-events": "Načítanie udalostí",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Ključna beseda", "keyword": "Ključna beseda",
"link-copied": "Povezava kopirana", "link-copied": "Povezava kopirana",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Ključna reč", "keyword": "Ključna reč",
"link-copied": "Линк је копиран", "link-copied": "Линк је копиран",
"loading": "Loading",
"loading-events": "Учитавање догађаја", "loading-events": "Учитавање догађаја",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Nyckelord", "keyword": "Nyckelord",
"link-copied": "Länk kopierad", "link-copied": "Länk kopierad",
"loading": "Loading",
"loading-events": "Laddar händelser", "loading-events": "Laddar händelser",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Anahtar Kelime", "keyword": "Anahtar Kelime",
"link-copied": "Bağlantı Kopyalandı", "link-copied": "Bağlantı Kopyalandı",
"loading": "Loading",
"loading-events": "Etkinlikler yükleniyor", "loading-events": "Etkinlikler yükleniyor",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "OCR verileri yükleniyor...", "loading-ocr-data": "OCR verileri yükleniyor...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Ключове слово", "keyword": "Ключове слово",
"link-copied": "Посилання скопійовано", "link-copied": "Посилання скопійовано",
"loading": "Завантаження",
"loading-events": "Завантаження подій", "loading-events": "Завантаження подій",
"loading-recipe": "Завантаження рецепта...", "loading-recipe": "Завантаження рецепта...",
"loading-ocr-data": "Завантаження даних OCR...", "loading-ocr-data": "Завантаження даних OCR...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "Keyword", "keyword": "Keyword",
"link-copied": "Link Copied", "link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "关键字", "keyword": "关键字",
"link-copied": "链接已复制", "link-copied": "链接已复制",
"loading": "Loading",
"loading-events": "正在加载事件", "loading-events": "正在加载事件",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -114,6 +114,7 @@
"json": "JSON", "json": "JSON",
"keyword": "關鍵字", "keyword": "關鍵字",
"link-copied": "已複製連結", "link-copied": "已複製連結",
"loading": "Loading",
"loading-events": "Loading Events", "loading-events": "Loading Events",
"loading-recipe": "Loading recipe...", "loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...", "loading-ocr-data": "Loading OCR data...",

View File

@ -130,40 +130,47 @@
<section class="mt-4"> <section class="mt-4">
<BaseCardSectionTitle class="pb-0" :icon="$globals.icons.cog" :title="$tc('settings.general-about')"> </BaseCardSectionTitle> <BaseCardSectionTitle class="pb-0" :icon="$globals.icons.cog" :title="$tc('settings.general-about')"> </BaseCardSectionTitle>
<v-card class="mb-4"> <v-card class="mb-4">
<template v-for="(property, idx) in appInfo"> <template v-if="appInfo && appInfo.length">
<v-list-item :key="property.name"> <template v-for="(property, idx) in appInfo">
<v-list-item-icon> <v-list-item :key="property.name">
<v-icon> {{ property.icon || $globals.icons.user }} </v-icon> <v-list-item-icon>
</v-list-item-icon> <v-icon> {{ property.icon || $globals.icons.user }} </v-icon>
<v-list-item-content> </v-list-item-icon>
<v-list-item-title> <v-list-item-content>
<div>{{ property.name }}</div> <v-list-item-title>
</v-list-item-title> <div>{{ property.name }}</div>
<template v-if="property.slot === 'recipe-scraper'"> </v-list-item-title>
<v-list-item-subtitle> <template v-if="property.slot === 'recipe-scraper'">
<a <v-list-item-subtitle>
target="_blank" <a
:href="`https://github.com/hhursev/recipe-scrapers/releases/tag/${property.value}`" target="_blank"
> :href="`https://github.com/hhursev/recipe-scrapers/releases/tag/${property.value}`"
>
{{ property.value }}
</a>
</v-list-item-subtitle>
</template>
<template v-else-if="property.slot === 'build'">
<v-list-item-subtitle>
<a target="_blank" :href="`https://github.com/hay-kot/mealie/commit/${property.value}`">
{{ property.value }}
</a>
</v-list-item-subtitle>
</template>
<template v-else>
<v-list-item-subtitle>
{{ property.value }} {{ property.value }}
</a> </v-list-item-subtitle>
</v-list-item-subtitle> </template>
</template> </v-list-item-content>
<template v-else-if="property.slot === 'build'"> </v-list-item>
<v-list-item-subtitle> <v-divider v-if="appInfo && idx !== appInfo.length - 1" :key="`divider-${property.name}`"></v-divider>
<a target="_blank" :href="`https://github.com/hay-kot/mealie/commit/${property.value}`"> </template>
{{ property.value }} </template>
</a> <template v-else>
</v-list-item-subtitle> <div class="mb-3 text-center">
</template> <AppLoader :waiting-text="$tc('general.loading')" />
<template v-else> </div>
<v-list-item-subtitle>
{{ property.value }}
</v-list-item-subtitle>
</template>
</v-list-item-content>
</v-list-item>
<v-divider v-if="appInfo && idx !== appInfo.length - 1" :key="`divider-${property.name}`"></v-divider>
</template> </template>
</v-card> </v-card>
</section> </section>
@ -186,6 +193,7 @@ import { useAdminApi, useUserApi } from "~/composables/api";
import { validators } from "~/composables/use-validators"; import { validators } from "~/composables/use-validators";
import { useAsyncKey } from "~/composables/use-utils"; import { useAsyncKey } from "~/composables/use-utils";
import { CheckAppConfig } from "~/lib/api/types/admin"; import { CheckAppConfig } from "~/lib/api/types/admin";
import AppLoader from "~/components/global/AppLoader.vue";
enum DockerVolumeState { enum DockerVolumeState {
Unknown = "unknown", Unknown = "unknown",
@ -208,294 +216,257 @@ interface CheckApp extends CheckAppConfig {
} }
export default defineComponent({ export default defineComponent({
layout: "admin", layout: "admin",
setup() { setup() {
// ========================================================== // ==========================================================
// Docker Volume Validation // Docker Volume Validation
const docker = reactive({ const docker = reactive({
loading: false, loading: false,
state: DockerVolumeState.Unknown, state: DockerVolumeState.Unknown,
}); });
async function dockerValidate() {
async function dockerValidate() { docker.loading = true;
docker.loading = true; // Do API Check
const { data } = await adminApi.about.checkDocker();
// Do API Check if (data == null) {
const { data } = await adminApi.about.checkDocker(); docker.state = DockerVolumeState.Error;
if (data == null) { return;
docker.state = DockerVolumeState.Error; }
return; // Get File Contents
} const { data: fileContents } = await adminApi.about.getDockerValidateFileContents();
if (data.text === fileContents) {
// Get File Contents docker.state = DockerVolumeState.Success;
const { data: fileContents } = await adminApi.about.getDockerValidateFileContents(); }
else {
if (data.text === fileContents) { docker.state = DockerVolumeState.Error;
docker.state = DockerVolumeState.Success; }
} else { docker.loading = false;
docker.state = DockerVolumeState.Error;
}
docker.loading = false;
}
const state = reactive({
loading: false,
address: "",
success: false,
error: "",
tested: false,
});
const appConfig = ref<CheckApp>({
emailReady: true,
baseUrlSet: true,
isSiteSecure: true,
isUpToDate: false,
ldapReady: false,
});
function isLocalHostOrHttps() {
return window.location.hostname === "localhost" || window.location.protocol === "https:";
}
const api = useUserApi();
const adminApi = useAdminApi();
onMounted(async () => {
const { data } = await adminApi.about.checkApp();
if (data) {
appConfig.value = { ...data, isSiteSecure: false };
}
appConfig.value.isSiteSecure = isLocalHostOrHttps();
});
const simpleChecks = computed<SimpleCheck[]>(() => {
const goodIcon = $globals.icons.checkboxMarkedCircle;
const badIcon = $globals.icons.alert;
const warningIcon = $globals.icons.alertCircle;
const goodColor = "success";
const badColor = "error";
const warningColor = "warning";
const data: SimpleCheck[] = [
{
id: "application-version",
text: i18n.t("settings.application-version"),
status: appConfig.value.isUpToDate,
errorText: i18n.t("settings.application-version-error-text", [rawAppInfo.value.version, rawAppInfo.value.versionLatest]),
successText: i18n.t("settings.mealie-is-up-to-date"),
color: appConfig.value.isUpToDate ? goodColor : warningColor,
icon: appConfig.value.isUpToDate ? goodIcon : warningIcon,
},
{
id: "secure-site",
text: i18n.t("settings.secure-site"),
status: appConfig.value.isSiteSecure,
errorText: i18n.t("settings.secure-site-error-text"),
successText: i18n.t("settings.secure-site-success-text"),
color: appConfig.value.isSiteSecure ? goodColor : badColor,
icon: appConfig.value.isSiteSecure ? goodIcon : badIcon,
},
{
id: "server-side-base-url",
text: i18n.t("settings.server-side-base-url"),
status: appConfig.value.baseUrlSet,
errorText:
i18n.t("settings.server-side-base-url-error-text"),
successText: i18n.t("settings.server-side-base-url-success-text"),
color: appConfig.value.baseUrlSet ? goodColor : badColor,
icon: appConfig.value.baseUrlSet ? goodIcon : badIcon,
},
{
id: "ldap-ready",
text: i18n.t("settings.ldap-ready"),
status: appConfig.value.ldapReady,
errorText:
i18n.t("settings.ldap-ready-error-text"),
successText: i18n.t("settings.ldap-ready-success-text"),
color: appConfig.value.ldapReady ? goodColor : warningColor,
icon: appConfig.value.ldapReady ? goodIcon : warningIcon,
},
];
return data;
});
async function testEmail() {
state.loading = true;
state.tested = false;
const { data } = await api.email.test({ email: state.address });
if (data) {
if (data.success) {
state.success = true;
} else {
state.error = data.error ?? "";
state.success = false;
} }
} const state = reactive({
state.loading = false; loading: false,
state.tested = true; address: "",
} success: false,
error: "",
const validEmail = computed(() => { tested: false,
if (state.address === "") { });
return false; const appConfig = ref<CheckApp>({
} emailReady: true,
const valid = validators.email(state.address); baseUrlSet: true,
isSiteSecure: true,
// Explicit bool check because validators.email sometimes returns a string isUpToDate: false,
if (valid === true) { ldapReady: false,
return true; });
} function isLocalHostOrHttps() {
return false; return window.location.hostname === "localhost" || window.location.protocol === "https:";
});
// ============================================================
// General About Info
const { $globals, i18n } = useContext();
const rawAppInfo = ref({
version: "null",
versionLatest: "null",
});
function getAppInfo() {
const statistics = useAsync(async () => {
const { data } = await adminApi.about.about();
if (data) {
rawAppInfo.value.version = data.version;
rawAppInfo.value.versionLatest = data.versionLatest;
const prettyInfo = [
{
name: i18n.t("about.version"),
icon: $globals.icons.information,
value: data.version,
},
{
slot: "build",
name: i18n.t("settings.build"),
icon: $globals.icons.information,
value: data.buildId,
},
{
name: i18n.t("about.application-mode"),
icon: $globals.icons.devTo,
value: data.production ? i18n.t("about.production") : i18n.t("about.development"),
},
{
name: i18n.t("about.demo-status"),
icon: $globals.icons.testTube,
value: data.demoStatus ? i18n.t("about.demo") : i18n.t("about.not-demo"),
},
{
name: i18n.t("about.api-port"),
icon: $globals.icons.api,
value: data.apiPort,
},
{
name: i18n.t("about.api-docs"),
icon: $globals.icons.file,
value: data.apiDocs ? i18n.t("general.enabled") : i18n.t("general.disabled"),
},
{
name: i18n.t("about.database-type"),
icon: $globals.icons.database,
value: data.dbType,
},
{
name: i18n.t("about.database-url"),
icon: $globals.icons.database,
value: data.dbUrl,
},
{
name: i18n.t("about.default-group"),
icon: $globals.icons.group,
value: data.defaultGroup,
},
{
slot: "recipe-scraper",
name: i18n.t("settings.recipe-scraper-version"),
icon: $globals.icons.primary,
value: data.recipeScraperVersion,
},
];
return prettyInfo;
} }
const api = useUserApi();
return data; const adminApi = useAdminApi();
}, useAsyncKey()); onMounted(async () => {
const { data } = await adminApi.about.checkApp();
return statistics; if (data) {
} appConfig.value = { ...data, isSiteSecure: false };
}
const appInfo = getAppInfo(); appConfig.value.isSiteSecure = isLocalHostOrHttps();
});
const bugReportDialog = ref(false); const simpleChecks = computed<SimpleCheck[]>(() => {
const goodIcon = $globals.icons.checkboxMarkedCircle;
const bugReportText = computed(() => { const badIcon = $globals.icons.alert;
const ignore = { const warningIcon = $globals.icons.alertCircle;
[i18n.tc("about.database-url")]: true, const goodColor = "success";
[i18n.tc("about.default-group")]: true, const badColor = "error";
}; const warningColor = "warning";
let text = "**Details**\n"; const data: SimpleCheck[] = [
{
appInfo.value?.forEach((item) => { id: "application-version",
if (ignore[item.name as string]) { text: i18n.t("settings.application-version"),
return; status: appConfig.value.isUpToDate,
errorText: i18n.t("settings.application-version-error-text", [rawAppInfo.value.version, rawAppInfo.value.versionLatest]),
successText: i18n.t("settings.mealie-is-up-to-date"),
color: appConfig.value.isUpToDate ? goodColor : warningColor,
icon: appConfig.value.isUpToDate ? goodIcon : warningIcon,
},
{
id: "secure-site",
text: i18n.t("settings.secure-site"),
status: appConfig.value.isSiteSecure,
errorText: i18n.t("settings.secure-site-error-text"),
successText: i18n.t("settings.secure-site-success-text"),
color: appConfig.value.isSiteSecure ? goodColor : badColor,
icon: appConfig.value.isSiteSecure ? goodIcon : badIcon,
},
{
id: "server-side-base-url",
text: i18n.t("settings.server-side-base-url"),
status: appConfig.value.baseUrlSet,
errorText: i18n.t("settings.server-side-base-url-error-text"),
successText: i18n.t("settings.server-side-base-url-success-text"),
color: appConfig.value.baseUrlSet ? goodColor : badColor,
icon: appConfig.value.baseUrlSet ? goodIcon : badIcon,
},
{
id: "ldap-ready",
text: i18n.t("settings.ldap-ready"),
status: appConfig.value.ldapReady,
errorText: i18n.t("settings.ldap-ready-error-text"),
successText: i18n.t("settings.ldap-ready-success-text"),
color: appConfig.value.ldapReady ? goodColor : warningColor,
icon: appConfig.value.ldapReady ? goodIcon : warningIcon,
},
];
return data;
});
async function testEmail() {
state.loading = true;
state.tested = false;
const { data } = await api.email.test({ email: state.address });
if (data) {
if (data.success) {
state.success = true;
}
else {
state.error = data.error ?? "";
state.success = false;
}
}
state.loading = false;
state.tested = true;
} }
text += `${item.name as string}: ${item.value as string}\n`; const validEmail = computed(() => {
}); if (state.address === "") {
return false;
const ignoreChecks: { [key: string]: boolean } = { }
"application-version": true, const valid = validators.email(state.address);
}; // Explicit bool check because validators.email sometimes returns a string
if (valid === true) {
text += "\n**Checks**\n"; return true;
}
simpleChecks.value.forEach((item) => { return false;
if (ignoreChecks[item.id]) { });
return; // ============================================================
// General About Info
const { $globals, i18n } = useContext();
const rawAppInfo = ref({
version: "null",
versionLatest: "null",
});
function getAppInfo() {
const statistics = useAsync(async () => {
const { data } = await adminApi.about.about();
if (data) {
rawAppInfo.value.version = data.version;
rawAppInfo.value.versionLatest = data.versionLatest;
const prettyInfo = [
{
name: i18n.t("about.version"),
icon: $globals.icons.information,
value: data.version,
},
{
slot: "build",
name: i18n.t("settings.build"),
icon: $globals.icons.information,
value: data.buildId,
},
{
name: i18n.t("about.application-mode"),
icon: $globals.icons.devTo,
value: data.production ? i18n.t("about.production") : i18n.t("about.development"),
},
{
name: i18n.t("about.demo-status"),
icon: $globals.icons.testTube,
value: data.demoStatus ? i18n.t("about.demo") : i18n.t("about.not-demo"),
},
{
name: i18n.t("about.api-port"),
icon: $globals.icons.api,
value: data.apiPort,
},
{
name: i18n.t("about.api-docs"),
icon: $globals.icons.file,
value: data.apiDocs ? i18n.t("general.enabled") : i18n.t("general.disabled"),
},
{
name: i18n.t("about.database-type"),
icon: $globals.icons.database,
value: data.dbType,
},
{
name: i18n.t("about.database-url"),
icon: $globals.icons.database,
value: data.dbUrl,
},
{
name: i18n.t("about.default-group"),
icon: $globals.icons.group,
value: data.defaultGroup,
},
{
slot: "recipe-scraper",
name: i18n.t("settings.recipe-scraper-version"),
icon: $globals.icons.primary,
value: data.recipeScraperVersion,
},
];
return prettyInfo;
}
return data;
}, useAsyncKey());
return statistics;
} }
const status = item.status ? i18n.tc("general.yes") : i18n.tc("general.no"); const appInfo = getAppInfo();
text += `${item.text.toString()}: ${status}\n`; const bugReportDialog = ref(false);
}); const bugReportText = computed(() => {
const ignore = {
text += `${i18n.tc("settings.email-configured")}: ${appConfig.value.emailReady ? i18n.tc("general.yes") : i18n.tc("general.no")}\n`; [i18n.tc("about.database-url")]: true,
text += `${i18n.tc("settings.docker-volume")}: ${docker.state}`; [i18n.tc("about.default-group")]: true,
};
return text; let text = "**Details**\n";
}); appInfo.value?.forEach((item) => {
if (ignore[item.name as string]) {
return { return;
bugReportDialog, }
bugReportText, text += `${item.name as string}: ${item.value as string}\n`;
DockerVolumeState, });
docker, const ignoreChecks: {
dockerValidate, [key: string]: boolean;
simpleChecks, } = {
appConfig, "application-version": true,
validEmail, };
validators, text += "\n**Checks**\n";
...toRefs(state), simpleChecks.value.forEach((item) => {
testEmail, if (ignoreChecks[item.id]) {
appInfo, return;
}; }
}, const status = item.status ? i18n.tc("general.yes") : i18n.tc("general.no");
head() { text += `${item.text.toString()}: ${status}\n`;
return { });
title: this.$t("settings.site-settings") as string, text += `${i18n.tc("settings.email-configured")}: ${appConfig.value.emailReady ? i18n.tc("general.yes") : i18n.tc("general.no")}\n`;
}; text += `${i18n.tc("settings.docker-volume")}: ${docker.state}`;
}, return text;
});
return {
bugReportDialog,
bugReportText,
DockerVolumeState,
docker,
dockerValidate,
simpleChecks,
appConfig,
validEmail,
validators,
...toRefs(state),
testEmail,
appInfo,
};
},
head() {
return {
title: this.$t("settings.site-settings") as string,
};
},
components: { AppLoader }
}); });
</script> </script>