Compare commits

...

350 Commits

Author SHA1 Message Date
Hayden
0800a8d00a chore: rewrite makefile in taskfile (#3035)
* add docker-compose with development dependencies

* delete old runtime.txt file

* specify specific group for postgres deps

* replace makefile with taskfile with new features

* drop template.env file in favor of defaults within taskfile

* use with github actions

* update docs for taskfile changes

* update task.json for vscode

* add taskfile to devcontainer.json

* pre-install taskfile so startup command works

* remove run command and fix desc for ui

* change node-> python->py for consistency
2024-01-27 18:14:42 +00:00
Jakub Urbańczyk
4d49e307e3 fix: running the container with PUID=0 and PGID=0 (#3030)
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-01-27 17:48:38 +00:00
Jason Hunter
20621a1950 update iOS guide (#3043)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-01-27 10:49:09 +00:00
boc-the-git
5a0b8940f5 Merge pull request #3053 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2024-01-27 08:32:05 +11:00
Hayden
c27d20b5c9 New translations en-us.json (Icelandic) 2024-01-26 15:26:14 -06:00
Hayden
5f24e87e84 New translations en-us.json (French, Canada) 2024-01-26 15:26:13 -06:00
Hayden
72980b3472 New translations en-us.json (English, United Kingdom) 2024-01-26 15:26:12 -06:00
Hayden
cedccf046b New translations en-us.json (Latvian) 2024-01-26 15:26:11 -06:00
Hayden
63514ab41c New translations en-us.json (Croatian) 2024-01-26 15:26:10 -06:00
Hayden
f8e8a40ec1 New translations en-us.json (Portuguese, Brazilian) 2024-01-26 15:26:08 -06:00
Hayden
65ce53fb17 New translations en-us.json (Galician) 2024-01-26 15:26:07 -06:00
Hayden
1352ae81c4 New translations en-us.json (Vietnamese) 2024-01-26 15:26:06 -06:00
Hayden
5c3b1f2890 New translations en-us.json (Chinese Simplified) 2024-01-26 15:26:05 -06:00
Hayden
555e341b65 New translations en-us.json (Ukrainian) 2024-01-26 15:26:04 -06:00
Hayden
6690ef8cab New translations en-us.json (Turkish) 2024-01-26 15:26:03 -06:00
Hayden
bbd6e7ef92 New translations en-us.json (Swedish) 2024-01-26 15:26:02 -06:00
Hayden
54a954164a New translations en-us.json (Serbian (Cyrillic)) 2024-01-26 15:26:01 -06:00
Hayden
d20ee21076 New translations en-us.json (Slovenian) 2024-01-26 15:26:00 -06:00
Hayden
ce7efd7505 New translations en-us.json (Slovak) 2024-01-26 15:25:59 -06:00
Hayden
04f0d33ca3 New translations en-us.json (Russian) 2024-01-26 15:25:58 -06:00
Hayden
01649de1e7 New translations en-us.json (Portuguese) 2024-01-26 15:25:57 -06:00
Hayden
3a739ba194 New translations en-us.json (Polish) 2024-01-26 15:25:56 -06:00
Hayden
5e78aa6e29 New translations en-us.json (Norwegian) 2024-01-26 15:25:55 -06:00
Hayden
a121fe9b55 New translations en-us.json (Dutch) 2024-01-26 15:25:53 -06:00
Hayden
d1c4a9b422 New translations en-us.json (Lithuanian) 2024-01-26 15:25:52 -06:00
Hayden
e0c72c5508 New translations en-us.json (Korean) 2024-01-26 15:25:51 -06:00
Hayden
4df4b7e0b6 New translations en-us.json (Japanese) 2024-01-26 15:25:50 -06:00
Hayden
e8d7c0423f New translations en-us.json (Hungarian) 2024-01-26 15:25:49 -06:00
Hayden
583151087e New translations en-us.json (Hebrew) 2024-01-26 15:25:48 -06:00
Hayden
c4b493564b New translations en-us.json (Finnish) 2024-01-26 15:25:47 -06:00
Hayden
e1aba3373a New translations en-us.json (Greek) 2024-01-26 15:25:46 -06:00
Hayden
ee65d7d67d New translations en-us.json (German) 2024-01-26 15:25:45 -06:00
Hayden
9029bccf5b New translations en-us.json (Danish) 2024-01-26 15:25:44 -06:00
Hayden
4fdf844485 New translations en-us.json (Czech) 2024-01-26 15:25:43 -06:00
Hayden
d2b1c2c5af New translations en-us.json (Catalan) 2024-01-26 15:25:42 -06:00
Hayden
84bd738ba5 New translations en-us.json (Bulgarian) 2024-01-26 15:25:41 -06:00
Hayden
e4eb4d3e3b New translations en-us.json (Arabic) 2024-01-26 15:25:40 -06:00
Hayden
422c485832 New translations en-us.json (Afrikaans) 2024-01-26 15:25:39 -06:00
Hayden
865ab6f843 New translations en-us.json (Spanish) 2024-01-26 15:25:38 -06:00
Hayden
cb948a8289 New translations en-us.json (French) 2024-01-26 15:25:37 -06:00
Hayden
8d38ef1fd7 New translations en-us.json (Romanian) 2024-01-26 15:25:36 -06:00
Hayden
2ae2475b8e New translations en-us.json (Italian) 2024-01-26 15:25:35 -06:00
Hayden
92c8b196de New translations en-us.json (Chinese Traditional) 2024-01-26 15:25:34 -06:00
renovate[bot]
e6ad0aad81 chore(deps): update dependency coverage to v7.4.1 (#3052)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-26 20:41:08 +00:00
Michael Genson
c6fbf8bce8 feat: Improvements To Adding A Recipe To A Shopping List (#3036)
* tweaked dialogs to make grammatical sense

* refactor ingredient rendering on recipe shopping list dialog
2024-01-26 15:27:36 +00:00
boc-the-git
8db5f7cce3 Merge pull request #3048 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2024-01-26 08:32:30 +11:00
Hayden
5f40064e2f New translations en-us.json (Chinese Simplified) 2024-01-25 15:26:25 -06:00
Hayden
e0cb6e0624 New translations en-us.json (Ukrainian) 2024-01-25 15:26:24 -06:00
Hayden
8bc73de815 New translations en-us.json (Turkish) 2024-01-25 15:26:22 -06:00
Hayden
71e2091130 New translations en-us.json (Czech) 2024-01-25 15:26:22 -06:00
Hayden
8e65a4c65a New translations en-us.json (Italian) 2024-01-25 15:26:20 -06:00
boc-the-git
34df20da81 Merge pull request #3047 from michael-genson/fix/bump-fastapi-and-refactor
fix: Bump FastAPI and Refactor
2024-01-26 07:20:15 +11:00
Michael Genson
254b6ae118 fixed breaking change with temp dir injection 2024-01-25 19:10:40 +00:00
Michael Genson
a840cb0800 patch missing client for testing 2024-01-25 19:10:21 +00:00
Michael Genson
098c8194f5 bump fastapi 2024-01-25 18:48:32 +00:00
renovate[bot]
1daf41f452 fix(deps): update dependency python-slugify to v8.0.2 (#3046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 12:41:33 -06:00
boc-the-git
72696cac20 Merge pull request #3045 from michael-genson/refactor/remove-passlib
refactor: Remove Passlib
2024-01-25 21:45:52 +11:00
Michael Genson
4d3ea5d231 remove passlib 2024-01-24 22:04:33 +00:00
Michael Genson
721063d091 Merge remote-tracking branch 'upstream/mealie-next' into refactor/remove-passlib 2024-01-24 22:03:54 +00:00
Michael Genson
4ae5c52de9 refactor to use bcrypt directly 2024-01-24 22:03:16 +00:00
renovate[bot]
58cb43e999 fix(deps): update dependency uvicorn to ^0.27.0 (#3034)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 21:56:56 +00:00
renovate[bot]
0b0c25d2f3 fix(deps): update dependency python-dotenv to v1.0.1 (#3038)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-24 15:44:36 -06:00
Hayden
21161dbf2e New Crowdin updates (#3044)
* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Italian)

* New translations en-us.json (Romanian)

* New translations en-us.json (French)

* New translations en-us.json (Spanish)

* New translations en-us.json (Afrikaans)

* New translations en-us.json (Arabic)

* New translations en-us.json (Bulgarian)

* New translations en-us.json (Catalan)

* New translations en-us.json (Czech)

* New translations en-us.json (Danish)

* New translations en-us.json (German)

* New translations en-us.json (Greek)

* New translations en-us.json (Finnish)

* New translations en-us.json (Hebrew)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Japanese)

* New translations en-us.json (Korean)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Polish)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

* New translations en-us.json (Slovak)

* New translations en-us.json (Slovenian)

* New translations en-us.json (Serbian (Cyrillic))

* New translations en-us.json (Swedish)

* New translations en-us.json (Turkish)

* New translations en-us.json (Ukrainian)

* New translations en-us.json (Chinese Simplified)

* New translations en-us.json (Vietnamese)

* New translations en-us.json (Galician)

* New translations en-us.json (Portuguese, Brazilian)

* New translations en-us.json (Croatian)

* New translations en-us.json (Latvian)

* New translations en-us.json (English, United Kingdom)

* New translations en-us.json (French, Canada)

* New translations en-us.json (Icelandic)

* New translations en-us.json (Turkish)
2024-01-24 21:37:05 +00:00
boc-the-git
f62feb8da2 Merge pull request #2717 from JackBailey/mealie-next
feat: Edit existing mealplan meals
2024-01-24 22:22:29 +11:00
boc-the-git
2cdbe816a6 Merge branch 'mealie-next' into mealie-next 2024-01-24 22:16:31 +11:00
boc-the-git
e32fddbc85 Merge pull request #3041 from mealie-recipes/renovate/mkdocs-material-9.x-lockfile
chore(deps): update dependency mkdocs-material to v9.5.5
2024-01-24 18:14:30 +11:00
renovate[bot]
8c17a81c91 chore(deps): update dependency mkdocs-material to v9.5.5 2024-01-24 06:43:56 +00:00
boc-the-git
d2188508fc Merge branch 'mealie-next' into mealie-next 2024-01-24 16:37:32 +11:00
boc-the-git
09dfca4f34 Provide write permissions for issues and PRs (#3039) 2024-01-23 06:37:45 -06:00
Michael Genson
61289416a5 feat: Add Ingredient Sections To Copied Ingredients (#3032)
* add ingredient title to copied text

* fix double linebreak

* lint

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-01-22 20:21:04 +00:00
Hayden
16d3dbef5d New Crowdin updates (#3033)
* New translations en-us.json (Swedish)

* New translations en-us.json (Galician)
2024-01-22 20:13:10 +00:00
Michael Genson
702907fc30 feat: Add Additional SMTP Headers to Decrease Spam Score (#3031)
* add html2text util

* add various missing email headers

* lint
2024-01-22 20:40:09 +01:00
boc-the-git
600c569ae8 feat: Remove debug mode from stale action (#3027) 2024-01-22 09:15:37 -06:00
boc-the-git
96995a4168 Merge pull request #3026 from Kuchenpirat/fix-site-maintenance-missing-words
fix: missing words in site maintenance
2024-01-22 20:51:15 +11:00
boc-the-git
3a9fd11344 Merge branch 'mealie-next' into fix-site-maintenance-missing-words 2024-01-22 20:45:13 +11:00
Kuchenpirat
62dffb622f fix missing words in site maintenance 2024-01-22 08:05:44 +00:00
Hayden
26dfc54d23 docs: new notes on releasing mealie (#3025)
* new notes on releasing mealie

---------

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-01-22 08:40:12 +01:00
boc-the-git
5d08647196 docs: Docker image versions (#3019)
* fix: Add the need to change image tag

* Minor tidying

* Advice to use specific tag, not latest

* Advice to use specific tag, not latest

* Revert mistake

* Reword

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-01-21 11:34:34 +01:00
boc-the-git
b51cd5d1c2 Merge pull request #3014 from cmintey/v1-doc-updates
Update docs to remove any mention of upcoming v1 release
2024-01-21 06:26:06 +11:00
Carter Mintey
4e66d5fb92 remove docs mentioning the impending v1 release 2024-01-20 19:15:21 +00:00
renovate[bot]
f0bbe796ce fix(deps): update dependency pydantic to v1.10.14 (#3008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-01-20 07:42:34 -09:00
Hayden
8d8a101580 chore: code gen (#3012)
* code-gen

* remove old generator commands

* remove old docker stuff
2024-01-20 16:34:57 +00:00
Hayden
0602d0bf00 note on network attached drives (#3013) 2024-01-20 16:20:11 +00:00
renovate[bot]
7e9c8b9e43 chore(deps): update dependency ruff to v0.1.14 (#3009)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-19 23:32:41 +00:00
Michael Genson
10ba4d2d7f feat: RTL Support for RTL Languages (Hebrew, Arabic) (#2832)
* add language direction to locale generation

* apply language direction when setting language

---------

Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
2024-01-19 16:56:36 +00:00
Kuchenpirat
d17e46ee50 chore(docs): change docker-compose to docker compose (#3007)
* update docker compose

* fix swag docker compose indentation

* and again....

* indentation for the second docker compose in the swag guide
2024-01-19 10:01:37 -06:00
Kuchenpirat
6acadbc52b Fix conditional check in createBackup function (#3006) 2024-01-19 09:10:31 -06:00
boc-the-git
943d65a2ac feat: Stale action tweaks (#3005)
* Increase operations-per-run for greater throughput

* Populate some default values for ease of reference

* Remove quotes from "bug: confirmed" label
2024-01-19 12:21:48 +01:00
renovate[bot]
521f309517 fix(deps): update dependency orjson to v3.9.12 (#3004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-18 19:07:18 +01:00
Hayden
f52eab6512 New translations en-us.json (Catalan) (#3003) 2024-01-18 11:25:58 -06:00
renovate[bot]
6358340df1 fix(deps): update dependency beautifulsoup4 to v4.12.3 (#3002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-17 18:15:28 +00:00
boc-the-git
f60c60be4d feat: Use GitHub actions for Stale detection (#3001)
* Introduce stale actions

* Remove outdated/redundant stale config
2024-01-17 09:56:41 -06:00
Michael Genson
2a5997a968 fix: Foreign Key Violations During Backup Restore (#2986)
* added more test data

* added missing pytest id

* add fk validation to backup restore

* removed bad type imports

* actually apply the invalid fk filter and clean up types

* fix key name

* added log when removing bad rows

* removed unused import

* bumped info to warning
2024-01-16 16:12:20 -06:00
renovate[bot]
b4c0a8b509 fix(deps): update dependency uvicorn to ^0.26.0 (#2999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 12:42:31 -06:00
renovate[bot]
8eae58e0c1 chore(deps): update dependency mkdocs-material to v9.5.4 (#2994)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-16 12:10:38 +01:00
boc-the-git
8d7659cf6a Remove outdated code factor badge (#2997) 2024-01-15 21:16:21 +01:00
Hayden
93932cc0c5 New Crowdin updates (#2995)
* New translations en-us.json (Italian)

* New translations en-us.json (English, United Kingdom)

* New translations en-us.json (Icelandic)

* New translations en-us.json (Icelandic)

* New translations en-us.json (Icelandic)

* New translations en-us.json (Icelandic)

* New translations en-us.json (Icelandic)
2024-01-15 09:49:30 -06:00
boc-the-git
5436e05d0e docs: Update image in example compose files (#2992)
* Change default image to RC2. Tidy up a bit

* Change default image to RC2. Tidy up a bit
2024-01-14 20:20:56 -06:00
Kuchenpirat
030588e5bb fix admin pages accessible by non admin users (#2988)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-01-14 16:03:31 +00:00
Hayden
43958527f4 New Crowdin updates (#2990)
* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Italian)

* New translations en-us.json (Romanian)

* New translations en-us.json (French)

* New translations en-us.json (Spanish)

* New translations en-us.json (Afrikaans)

* New translations en-us.json (Arabic)

* New translations en-us.json (Bulgarian)

* New translations en-us.json (Catalan)

* New translations en-us.json (Czech)

* New translations en-us.json (Danish)

* New translations en-us.json (German)

* New translations en-us.json (Greek)

* New translations en-us.json (Finnish)

* New translations en-us.json (Hebrew)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Japanese)

* New translations en-us.json (Korean)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Polish)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

* New translations en-us.json (Slovak)

* New translations en-us.json (Slovenian)

* New translations en-us.json (Serbian (Cyrillic))

* New translations en-us.json (Swedish)

* New translations en-us.json (Turkish)

* New translations en-us.json (Ukrainian)

* New translations en-us.json (Chinese Simplified)

* New translations en-us.json (Vietnamese)

* New translations en-us.json (Galician)

* New translations en-us.json (Portuguese, Brazilian)

* New translations en-us.json (Croatian)

* New translations en-us.json (Latvian)

* New translations en-us.json (English, United Kingdom)

* New translations en-us.json (French, Canada)
2024-01-14 16:52:59 +01:00
RealFoxie
4357c37d22 Add extra button "Next step" to ingredient linker dialog (#2920)
* Add "next step" button to ingredient linker dialog

clicking button will save current step ingredient links and show the next step in the dialog

* unload ingredient linker dialog to reset scroll position

* cleanup forward button in linking ingredients dialog

* add vertical spacing between buttons on smaller screens - recipe linker dialog

* align buttons equally to match alignment of `cancel`

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-01-13 15:45:59 +00:00
renovate[bot]
631500a574 chore(deps): update dependency ruff to v0.1.13 (#2983)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-12 10:15:14 -06:00
Guillaume
e7eb2a8dc5 Fixing typo in ghcr.io domain (#2982) 2024-01-12 16:04:08 +00:00
Hayden
5418223425 New translations en-us.json (Hungarian) (#2981) 2024-01-11 21:35:38 -06:00
renovate[bot]
940df92222 chore(deps): update dependency ruff to v0.1.12 (#2978)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-11 17:38:46 -06:00
Hayden
21c4ba54a5 fix: docker health checks (#2979) 2024-01-11 23:11:42 +00:00
Hayden
58df50f624 fix: re-enable change user and fix SIGTERM (#2882)
* re-enable change user

* fix gosu and close #2723
2024-01-11 14:03:08 -09:00
Michael Genson
9c0ce1af27 bump tzdata (#2976) 2024-01-11 08:18:17 +01:00
renovate[bot]
b8dd0fc97c chore(deps): update dependency mypy to v1.8.0 (#2943)
* chore(deps): update dependency mypy to v1.8.0

* appease the linting overlords

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-01-11 04:44:23 +00:00
renovate[bot]
0d89c86559 fix(deps): update dependency jinja2 to v3.1.3 (#2975)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 22:23:39 -06:00
renovate[bot]
ce929f2833 chore(deps): update dependency ruff to ^0.1.0 (#2944)
* chore(deps): update dependency ruff to ^0.1.0

* fix breaking cfg change

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-01-11 00:53:54 +00:00
boc-the-git
1d7fc0a945 Merge pull request #2974 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2024-01-11 11:15:40 +11:00
Hayden
c64ae94043 New translations en-us.json (Chinese Traditional) 2024-01-10 17:55:18 -06:00
Hayden
1aa9bbe8e2 New translations en-us.json (Turkish) 2024-01-10 17:55:17 -06:00
Hayden
9b97a7b3e7 New translations en-us.json (Russian) 2024-01-10 17:55:16 -06:00
Hayden
60eabf3e1c New translations en-us.json (Portuguese) 2024-01-10 17:55:15 -06:00
Hayden
1f195a02a0 New translations en-us.json (Italian) 2024-01-10 17:55:14 -06:00
renovate[bot]
238ef838ba fix(deps): update dependency lxml to v5 (#2973)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 16:42:29 -06:00
renovate[bot]
04d2fa2416 fix(deps): update dependency gunicorn to v21 (#2972)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 21:55:58 +00:00
renovate[bot]
b79c0ad441 fix(deps): update dependency uvicorn to ^0.25.0 (#2971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 15:44:23 -06:00
renovate[bot]
ab3c9e8bf0 fix(deps): update dependency rapidfuzz to v3.6.1 (#2968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 21:29:39 +00:00
renovate[bot]
72e78feaad fix(deps): update dependency pillow to v10.2.0 (#2967)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 15:18:28 -06:00
renovate[bot]
da71ee5deb fix(deps): update dependency extruct to ^0.16.0 (#2966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 21:05:04 +00:00
renovate[bot]
e0fca5e4b7 fix(deps): update dependency apprise to v1.7.1 (#2964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 14:53:57 -06:00
renovate[bot]
2951042e7a fix(deps): update dependency alembic to v1.13.1 (#2963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 20:30:17 +00:00
renovate[bot]
3cf9d40326 fix(deps): update dependency aiofiles to v23.2.1 (#2962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 14:18:45 -06:00
renovate[bot]
bc44cdf16f chore(deps): update dependency rich to v13.7.0 (#2961)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 20:03:13 +00:00
renovate[bot]
b41509bea0 fix(deps): update dependency psycopg2-binary to v2.9.9 (#2939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 13:51:45 -06:00
Carter
53b790b18c Remove options that no longer exist and update a few options to be more reasonable (#2951)
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-01-11 06:42:04 +11:00
boc-the-git
00abd43fab docs: Add 'latest release' to README (#2958) 2024-01-10 19:21:21 +00:00
renovate[bot]
e37426d5d8 chore(deps): update dependency pylint to v3 (#2965)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 18:58:50 +00:00
renovate[bot]
aed9f0047c chore(deps): update dependency pre-commit to v3.6.0 (#2953)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 12:40:43 -06:00
renovate[bot]
9b365f882a chore(deps): update dependency pytest-asyncio to ^0.23.0 (#2957)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 12:29:39 -06:00
renovate[bot]
b51da29e79 chore(deps): update dependency black to v23.12.1 (#2952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 12:13:07 -06:00
renovate[bot]
edb9c517c7 chore(deps): update dependency pytest to v7.4.4 (#2956)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 17:50:56 +00:00
renovate[bot]
d3b3bb63a6 fix(deps): update dependency orjson to v3.9.10 (#2948)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2024-01-10 11:37:33 -06:00
Kuchenpirat
0bf09ad910 fix (dev): Cors middleware (#2960)
* add cors middleware

* 🧹
2024-01-10 17:20:22 +00:00
Carter
36de815f50 Update some broken links in the FAQ docs (#2955)
* update some broken links in the faq

* update api reference

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-01-10 22:01:19 +11:00
renovate[bot]
d756f3a057 fix(deps): update dependency httpx to ^0.26.0 (#2947)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 02:08:57 +00:00
renovate[bot]
40172ed62e fix(deps): update dependency bcrypt to v4.1.2 (#2945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 01:56:11 +00:00
renovate[bot]
5ca340a0bd fix(deps): update dependency python-ldap to v3.4.4 (#2949)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 01:41:03 +00:00
renovate[bot]
8fc7ab8c0a fix(deps): update dependency pydantic to v1.10.13 (#2940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 01:28:37 +00:00
renovate[bot]
52af8d6132 chore(deps): update dependency mkdocs-material to v9.5.3 (#2942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 01:17:45 +00:00
renovate[bot]
fcd00b6f5b chore(deps): update dependency coverage to v7.4.0 (#2941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 01:03:08 +00:00
renovate[bot]
8c9b513d21 fix(deps): update dependency lxml to v4.9.4 (#2937)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-10 00:46:59 +00:00
Hayden
c7c96531f9 New Crowdin updates (#2938)
* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Italian)

* New translations en-us.json (Romanian)

* New translations en-us.json (French)

* New translations en-us.json (Spanish)

* New translations en-us.json (Afrikaans)

* New translations en-us.json (Arabic)

* New translations en-us.json (Bulgarian)

* New translations en-us.json (Catalan)

* New translations en-us.json (Czech)

* New translations en-us.json (Danish)

* New translations en-us.json (German)

* New translations en-us.json (Greek)

* New translations en-us.json (Finnish)

* New translations en-us.json (Hebrew)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Japanese)

* New translations en-us.json (Korean)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Polish)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

* New translations en-us.json (Slovak)

* New translations en-us.json (Slovenian)

* New translations en-us.json (Serbian (Cyrillic))

* New translations en-us.json (Swedish)

* New translations en-us.json (Turkish)

* New translations en-us.json (Ukrainian)

* New translations en-us.json (Chinese Simplified)

* New translations en-us.json (Vietnamese)

* New translations en-us.json (Galician)

* New translations en-us.json (Portuguese, Brazilian)

* New translations en-us.json (Croatian)

* New translations en-us.json (Latvian)

* New translations en-us.json (English, United Kingdom)

* New translations en-us.json (French, Canada)
2024-01-09 18:40:24 -06:00
renovate[bot]
ca7646dde4 chore(deps): update dependency pylint to v2.17.7 (#2936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-09 21:58:25 +00:00
boc-the-git
2bb2106251 feat: Implement security scans using CodeQL (#2793)
* Create codeql.yml

* Add security scanning to PR tests

* Revert "Add security scanning to PR tests"

This reverts commit 44d180bd43. The CodeQL file already responds to pull requests.

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-01-09 12:52:06 -09:00
Hayden
3cf76a64e7 chore: disable npm renovate (redo) (#2935) 2024-01-09 12:11:31 -09:00
Hayden
031ba075d8 chore: disable npm renovate (#2933) 2024-01-09 15:03:04 -06:00
Michael Genson
1e4fde88ed feat: Add Suggestion To Use Bulk URL Import (#2852)
* fixed warning

* re-arranged menu options

* fixed broken link

* added bulk url suggestion

* simply bulk import url var

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2024-01-09 14:37:22 +00:00
Hayden
143f44ad68 New Crowdin updates (#2929)
* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Italian)

* New translations en-us.json (Romanian)

* New translations en-us.json (French)

* New translations en-us.json (Spanish)

* New translations en-us.json (Afrikaans)

* New translations en-us.json (Arabic)

* New translations en-us.json (Bulgarian)

* New translations en-us.json (Catalan)

* New translations en-us.json (Czech)

* New translations en-us.json (Danish)

* New translations en-us.json (German)

* New translations en-us.json (Greek)

* New translations en-us.json (Finnish)

* New translations en-us.json (Hebrew)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Japanese)

* New translations en-us.json (Korean)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Polish)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

* New translations en-us.json (Slovak)

* New translations en-us.json (Slovenian)

* New translations en-us.json (Serbian (Cyrillic))

* New translations en-us.json (Swedish)

* New translations en-us.json (Turkish)

* New translations en-us.json (Ukrainian)

* New translations en-us.json (Chinese Simplified)

* New translations en-us.json (Vietnamese)

* New translations en-us.json (Galician)

* New translations en-us.json (Portuguese, Brazilian)

* New translations en-us.json (Croatian)

* New translations en-us.json (Latvian)

* New translations en-us.json (English, United Kingdom)

* New translations en-us.json (French, Canada)
2024-01-09 00:52:18 +01:00
boc-the-git
29c3cb2296 Add unique IDs to recipe notes (#2919) 2024-01-08 20:47:09 +00:00
Michael Genson
8ca5a9454e fix: Migration Failure On Postgres Due To Foreign Key Error (#2923)
* added test data to (hopefully) expose fk error

* added additional commits during migration

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2024-01-08 11:01:44 +00:00
boc-the-git
b1fa089236 Merge pull request #2906 from Kuchenpirat/organizertype-delete-prompt
Update Recipe Organizer Delete Prompt Text
2024-01-08 21:24:23 +11:00
Kuchenpirat
6e8b84be80 include OrganizerType in delete prompt
Co-authored-by: Elijah Mock <28277163+ekcom@users.noreply.github.com>
2024-01-08 10:45:37 +01:00
boc-the-git
00db83d218 Merge branch 'mealie-next' into organizertype-delete-prompt 2024-01-08 20:15:11 +11:00
boc-the-git
82eda8f630 Merge pull request #2924 from michael-genson/fix/broken-image-on-test-email
fix: Updated Image References To Use Newer Commit And Fixed Email Banner
2024-01-08 20:12:30 +11:00
Michael Genson
f1559e5512 fix broken email banner 2024-01-08 03:46:24 +00:00
Michael Genson
8cf1700d03 update old raw.githubusercontent links 2024-01-08 03:45:13 +00:00
Hayden
9571816ac4 New Crowdin updates (#2915)
* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Norwegian)
2024-01-06 23:12:54 +00:00
boc-the-git
ccaf7b0279 Merge pull request #2909 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2024-01-06 10:04:40 +11:00
Hayden
0b05239064 New translations en-us.json (Hungarian) 2024-01-05 16:59:00 -06:00
Hayden
4cb07aa09f New translations en-us.json (French, Canada) 2024-01-05 16:58:59 -06:00
Hayden
d3c9f928b1 New translations en-us.json (Norwegian) 2024-01-05 16:58:58 -06:00
Hayden
d3dac815f6 New translations en-us.json (Hungarian) 2024-01-05 16:58:57 -06:00
Hayden
7e8388dec9 New translations en-us.json (Romanian) 2024-01-05 16:58:56 -06:00
boc-the-git
b80ed443c9 Merge pull request #2907 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2024-01-05 10:00:49 +11:00
Hayden
089c24cbda New translations en-us.json (Romanian) 2024-01-04 16:06:49 -06:00
Kuchenpirat
71b3989eec fix manageData Label delete Prompt 2024-01-04 12:00:21 +00:00
Kuchenpirat
6b3266e470 Merge branch 'mealie-next' into organizertype-delete-prompt 2024-01-04 12:56:16 +01:00
Kuchenpirat
ac9f335e59 update texts 2024-01-04 11:44:04 +00:00
Hayden
44ca79e7cd New translations en-us.json (Arabic) (#2903) 2024-01-03 16:22:24 +01:00
Michael Genson
7602c67449 fix: More Backup Restore Fixes (#2859)
* refactor normalized search migration to use dummy default

* changed group slug migration to use raw SQL

* updated comment

* added tests with anonymized backups (currently failing)

* typo

* fixed LDAP enum in test data

* fix for adding label settings across groups

* add migration data fixes

* fix shopping list label settings test

* re-run db init instead of just running alembic migration, to include fixes

* intentionally broke SQLAlchemy GUID handling

* safely convert between GUID types in different databases

* restore original test data after testing backup restores

* added missing group name update to migration
2024-01-03 04:19:04 +00:00
Michael Genson
b3f7f2d89f docs: Update Public Recipe and 0.5.x Migration Docs (#2901)
* added more info regarding public recipes

* fix broken info tag

* added more information to the 0.5.x migration

* added email banner to frontend codebase
2024-01-02 22:13:20 -06:00
Hayden
76f7ebd12b New Crowdin updates (#2900)
* New translations en-us.json (Norwegian)

* New translations en-us.json (Chinese Simplified)
2024-01-02 14:35:26 +00:00
Hayden
36ca540033 New Crowdin updates (#2895)
* New translations en-us.json (Hungarian)

* New translations en-us.json (Chinese Simplified)
2024-01-01 15:57:54 +01:00
Hayden
f63c469bf5 New Crowdin updates (#2894)
* New translations en-us.json (Italian)

* New translations en-us.json (French)
2023-12-31 14:03:24 +00:00
boc-the-git
33eecbbd13 Merge pull request #2890 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-31 01:11:15 +11:00
Hayden
a4880c5d95 New translations en-us.json (French, Canada) 2023-12-30 07:48:37 -06:00
Hayden
29525ed54e New translations en-us.json (English, United Kingdom) 2023-12-30 07:48:36 -06:00
Hayden
2d85c15153 New translations en-us.json (Latvian) 2023-12-30 07:48:35 -06:00
Hayden
c04025b5f2 New translations en-us.json (Croatian) 2023-12-30 07:48:34 -06:00
Hayden
e53c5ca0f0 New translations en-us.json (Portuguese, Brazilian) 2023-12-30 07:48:33 -06:00
Hayden
d5252796ac New translations en-us.json (Galician) 2023-12-30 07:48:32 -06:00
Hayden
f36093c023 New translations en-us.json (Vietnamese) 2023-12-30 07:48:31 -06:00
Hayden
234ae8732d New translations en-us.json (Chinese Simplified) 2023-12-30 07:48:30 -06:00
Hayden
01f4abc0ad New translations en-us.json (Ukrainian) 2023-12-30 07:48:29 -06:00
Hayden
00f5cc2dd2 New translations en-us.json (Turkish) 2023-12-30 07:48:28 -06:00
Hayden
02d71eb36e New translations en-us.json (Swedish) 2023-12-30 07:48:27 -06:00
Hayden
3ac9d9bce7 New translations en-us.json (Serbian (Cyrillic)) 2023-12-30 07:48:26 -06:00
Hayden
13180718c7 New translations en-us.json (Slovenian) 2023-12-30 07:48:25 -06:00
Hayden
9f18d63b8a New translations en-us.json (Slovak) 2023-12-30 07:48:24 -06:00
Hayden
d4c70d66c5 New translations en-us.json (Russian) 2023-12-30 07:48:23 -06:00
Hayden
270c70ce87 New translations en-us.json (Portuguese) 2023-12-30 07:48:22 -06:00
Hayden
134812e4ce New translations en-us.json (Polish) 2023-12-30 07:48:21 -06:00
Hayden
7aecaf58b1 New translations en-us.json (Norwegian) 2023-12-30 07:48:20 -06:00
Hayden
7e9b429e7d New translations en-us.json (Dutch) 2023-12-30 07:48:19 -06:00
Hayden
07f96ca6a5 New translations en-us.json (Lithuanian) 2023-12-30 07:48:18 -06:00
Hayden
ae7d3d7e53 New translations en-us.json (Korean) 2023-12-30 07:48:17 -06:00
Hayden
e11614fba3 New translations en-us.json (Japanese) 2023-12-30 07:48:16 -06:00
Hayden
5837ca4ce7 New translations en-us.json (Hungarian) 2023-12-30 07:48:15 -06:00
Hayden
5b778e7a7d New translations en-us.json (Hebrew) 2023-12-30 07:48:14 -06:00
Hayden
783aa77b9b New translations en-us.json (Finnish) 2023-12-30 07:48:13 -06:00
Hayden
b6a2dd51ea New translations en-us.json (Greek) 2023-12-30 07:48:12 -06:00
Hayden
99c9be0778 New translations en-us.json (German) 2023-12-30 07:48:11 -06:00
Hayden
a8dfc1680a New translations en-us.json (Danish) 2023-12-30 07:48:10 -06:00
Hayden
fe9803e2a5 New translations en-us.json (Czech) 2023-12-30 07:48:09 -06:00
Hayden
4d2ca15163 New translations en-us.json (Catalan) 2023-12-30 07:48:07 -06:00
Hayden
299dc1a40d New translations en-us.json (Bulgarian) 2023-12-30 07:48:06 -06:00
Hayden
ae5e557df9 New translations en-us.json (Arabic) 2023-12-30 07:48:05 -06:00
Hayden
1e30462eaf New translations en-us.json (Afrikaans) 2023-12-30 07:48:04 -06:00
Hayden
10e4c17592 New translations en-us.json (Spanish) 2023-12-30 07:48:03 -06:00
Hayden
0726ce828c New translations en-us.json (French) 2023-12-30 07:48:02 -06:00
Hayden
2dd80bd8dc New translations en-us.json (Romanian) 2023-12-30 07:48:01 -06:00
Hayden
8fa5065814 New translations en-us.json (Italian) 2023-12-30 07:48:00 -06:00
Hayden
972610d1de New translations en-us.json (Chinese Traditional) 2023-12-30 07:47:59 -06:00
Kuchenpirat
6a71af98bd fix: show copy to clipboard failure (#2886)
* fix for AppButtonCopy

* add some logging

* fix if statement

* refactor, use .then

* check for copied

* Fix recipe share link

* refactor AppButtonCopy

* update tooltip text

* update use-copy

* logging

* fix is supported check

* more fixes for use-copy.ts

---------

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2023-12-29 15:48:28 +00:00
Hayden
f6f9a1c532 New translations en-us.json (Italian) (#2887) 2023-12-29 13:53:02 +00:00
Hayden
46cb36212a New Crowdin updates (#2883)
* New translations en-us.json (Greek)

* New translations en-us.json (Greek)

* New translations en-us.json (Greek)

* New translations en-us.json (Chinese Simplified)

* New translations en-us.json (Chinese Traditional)

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-28 13:52:29 +00:00
Hayden
ece11a5f39 fix: remove username/password banner (#2881)
* remove username/password banner

* remove divider
2023-12-27 13:19:12 -09:00
boc-the-git
73559bea91 fix: Example file using wrong volume declaration (#2872)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2023-12-27 20:03:49 +00:00
boc-the-git
9809167188 fix: Remove references to 'advanced' installation guide (#2880) 2023-12-27 11:27:51 +01:00
Hayden
a1b36d9361 New translations en-us.json (Chinese Simplified) (#2878) 2023-12-26 17:08:42 +01:00
Hayden
f89586894c New translations en-us.json (Chinese Simplified) (#2877) 2023-12-25 19:14:10 +01:00
Hayden
408718558b New Crowdin updates (#2876)
* New translations en-us.json (Italian)

* New translations en-us.json (Swedish)

* New translations en-us.json (Chinese Simplified)
2023-12-24 19:42:11 +01:00
boc-the-git
342fa6128e Merge pull request #2871 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-22 22:27:50 +11:00
Hayden
331bff84c3 New translations en-us.json (Swedish) 2023-12-22 05:21:00 -06:00
Hayden
88aa8b2edb New translations en-us.json (Chinese Simplified) 2023-12-22 05:20:58 -06:00
boc-the-git
8aa9402e1d Merge pull request #2870 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-21 22:17:45 +11:00
Hayden
86ba7dcf23 New translations en-us.json (Chinese Simplified) 2023-12-21 05:12:39 -06:00
Hayden
7222abe244 security: arbitrary file download by authenticated user (#2867)
* restricts download tokens to data directory

* block requests outside of the data dir
2023-12-19 18:34:34 -09:00
boc-the-git
fae8484f84 Re-enable cache (#2865) 2023-12-19 11:42:29 +00:00
boc-the-git
47d955adca Merge pull request #2864 from Kuchenpirat/bump-recipe-scrapers-14.53
chore: bump recipe-scrapers
2023-12-19 19:37:12 +11:00
Kuchenpirat
e5a19b3a49 bump recipe-scrapers 2023-12-19 08:18:56 +00:00
boc-the-git
d31293405c Merge pull request #2858 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-18 09:14:31 +11:00
Hayden
faf743ecf3 New translations en-us.json (Norwegian) 2023-12-17 16:03:09 -06:00
Hayden
aa44fef36f New translations en-us.json (Norwegian) 2023-12-17 16:03:09 -06:00
Hayden
0ccbb415b4 New translations en-us.json (Norwegian) 2023-12-17 16:03:08 -06:00
Hayden
b31647b52b New translations en-us.json (Chinese Simplified) 2023-12-17 16:03:07 -06:00
Hayden
0aba36e17f New translations en-us.json (Polish) 2023-12-17 16:03:05 -06:00
Hayden
954ee3dc19 New translations en-us.json (Norwegian) 2023-12-17 16:03:04 -06:00
Hayden
1d72178b93 New translations en-us.json (Bulgarian) 2023-12-17 16:03:03 -06:00
Hayden
36712cdb14 New Crowdin updates (#2855)
* New translations en-us.json (Italian)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Portuguese)
2023-12-16 21:58:32 +00:00
Hayden
2236a93a91 New translations en-us.json (Norwegian) (#2851) 2023-12-15 22:03:12 +00:00
Carter
46c1a2f0b8 add libldap-2.5 to runtime dependencies (#2849) 2023-12-15 11:38:09 -06:00
Hayden
bbd8dd6e73 hotfix: disable cache (#2848) 2023-12-15 06:05:28 -09:00
Michael Genson
ca9f66ee24 feat: Remove OCR Support (#2838)
* remove ocr package

* remove tesseract

* remove OCR from app

* remove OCR from tests

* fix docs
2023-12-14 17:26:43 -09:00
Hayden
c48680374d New Crowdin updates (#2844)
* New translations en-us.json (Romanian)

* New translations en-us.json (French)

* New translations en-us.json (Spanish)

* New translations en-us.json (Afrikaans)

* New translations en-us.json (Arabic)

* New translations en-us.json (Bulgarian)

* New translations en-us.json (Czech)

* New translations en-us.json (Danish)

* New translations en-us.json (German)

* New translations en-us.json (Greek)

* New translations en-us.json (Finnish)

* New translations en-us.json (Hebrew)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Italian)

* New translations en-us.json (Japanese)

* New translations en-us.json (Korean)

* New translations en-us.json (Lithuanian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Norwegian)

* New translations en-us.json (Polish)

* New translations en-us.json (Portuguese)

* New translations en-us.json (Russian)

* New translations en-us.json (Slovak)

* New translations en-us.json (Slovenian)

* New translations en-us.json (Serbian (Cyrillic))

* New translations en-us.json (Swedish)

* New translations en-us.json (Turkish)

* New translations en-us.json (Ukrainian)

* New translations en-us.json (Chinese Simplified)

* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Vietnamese)

* New translations en-us.json (Galician)

* New translations en-us.json (Portuguese, Brazilian)

* New translations en-us.json (Croatian)

* New translations en-us.json (Latvian)

* New translations en-us.json (English, United Kingdom)

* New translations en-us.json (French, Canada)

---------

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2023-12-14 22:09:01 +00:00
Elijah Mock
c5f2df57f6 fix: Copyedit getting started docs (#2840)
* Correct grammar and typo

* Correct grammar

* Fix some words

* Correct formatting

* Correct grammar on v1 migration page

* Correct grammar, punctuation, and typos in faq

* Fix grammar in installation-checklist

* One last correction
2023-12-13 21:43:48 -06:00
boc-the-git
07037011ae fix: Tidy up grammar (#2836) 2023-12-13 14:50:35 +00:00
Michael Genson
861e8ac564 fix: Paprika Migration Error (#2834)
* made migration more fault tolerant

* added edgecase for recipes with no ings/instructions

* keep log for debugging

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-13 10:31:13 +00:00
Hayden
92d955172d New Crowdin updates (#2833)
* New translations en-us.json (Norwegian)

* New translations en-us.json (Arabic)

* New translations en-us.json (Swedish)

* New translations en-us.json (Chinese Traditional)

* New translations en-us.json (Arabic)
2023-12-12 16:21:17 -06:00
boc-the-git
b13d66108d Merge branch 'mealie-next' into mealie-next 2023-12-12 21:32:26 +11:00
Hayden
ce5b31dce5 New Crowdin updates (#2831)
* New translations en-us.json (Norwegian)

* New translations en-us.json (Dutch)

* New translations en-us.json (Arabic)

* New translations en-us.json (Bulgarian)

* New translations en-us.json (Hungarian)

* New translations en-us.json (Turkish)

* New translations en-us.json (Portuguese, Brazilian)

* New translations en-us.json (Arabic)

* New translations en-us.json (Arabic)

* New translations en-us.json (Portuguese, Brazilian)
2023-12-11 15:31:16 -06:00
Michael Genson
b1d26283ba docs: add crowdin info to docs (#2828) 2023-12-12 06:22:10 +11:00
Kuchenpirat
ef2918c85b cleanup: remove docker volume validation (#2830)
* remove docker volume validation

* remove imports & linting

* remove missed
2023-12-11 18:49:26 +00:00
Michael Genson
95bb6d0184 chore: removed unused analytics page (#2829) 2023-12-11 18:27:05 +00:00
Michael Genson
8311db7e60 fix: Prevent Creating Groups With No Name (#2803)
* prevent creating groups with no name

* add db fix fro groups with no name

* moved non-actionable fix logs to debug level

* 🧹

* use id as default name to avoid collisions

* simplified group name constraint

* removed redundant import
2023-12-11 09:20:57 -09:00
Trenton H
c3ec875d59 chore: remove GitHub Actions deprecated command (#2790)
* Resolves GitHub Actions usage of deprecated command for setting output

* Updates action versions to the latest released versions

---------

Co-authored-by: Trenton Holmes <trenton.holmes@psware.com>
2023-12-11 14:57:13 +00:00
Dany Marcoux
0151527fe3 Use */mealie-recipes/mealie in all Markdown files (#2827)
This replaces the older */hay-kot/mealie

The changes were done with:

sed -i -e 's|/hay-kot/mealie|/mealie-recipes/mealie|g' **/*.md
2023-12-11 14:03:11 +01:00
boc-the-git
787e2885b3 Minor enhancements (#2826) 2023-12-11 10:32:06 +00:00
Michael Genson
c9c568570f fix cursor on recipe components (#2819)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-11 09:58:05 +00:00
Michael Genson
c226e71c17 overwrite history to avoid duplicating recipe imports (#2820)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-11 09:36:45 +00:00
Michael Genson
c6263a38ab remove experimental banner (#2816)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-11 09:27:42 +00:00
Michael Genson
5a153b178d fix: Better Scraper Image Processing (#2821)
* add additional case for scraped image parsing

* made scraper more fault tolerant for missing images

* re-ordered case to favor better implementations

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-11 09:22:06 +00:00
boc-the-git
a6ec488864 Merge pull request #2822 from michael-genson/fix/add-url-to-opengraph-scraper
fix: Use Scraped URL instead of Open Graph URL when importing a recipe via URL
2023-12-11 15:57:28 +11:00
boc-the-git
e5f278fdbb Merge branch 'mealie-next' into fix/add-url-to-opengraph-scraper 2023-12-11 15:52:12 +11:00
Michael Genson
275bb9617a use scraper URL instead of og tag 2023-12-11 04:37:00 +00:00
boc-the-git
32616a8c2b Merge pull request #2818 from boc-the-git/tidy-site-settings
fix: Rearrange defineComponent to remove warnings
2023-12-11 14:54:02 +11:00
boc-the-git
2ca21d3bbc Merge pull request #2817 from michael-genson/chore/fix-nuxt-hot-reload
fix: Dev Settings and Nuxt Hot Reload
2023-12-11 14:42:44 +11:00
boc-the-git
39240a401f Merge branch 'mealie-next' into chore/fix-nuxt-hot-reload 2023-12-11 14:37:38 +11:00
Michael Genson
e7e5c9d62d fix dev settings 2023-12-11 03:28:00 +00:00
boc-the-git
780f0b9ca5 Merge pull request #2815 from michael-genson/fix/meal-plan-settings-offscreen
fix: Wrap Mealplan Settings
2023-12-11 12:38:53 +11:00
boc-the-git
36b35caffb Merge branch 'mealie-next' into fix/meal-plan-settings-offscreen 2023-12-11 12:33:41 +11:00
Michael Genson
f38541588a wrap mealplan settings 2023-12-11 01:20:28 +00:00
boc-the-git
0b6549c90a Merge pull request #2814 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-11 08:27:17 +11:00
Hayden
2986eb3498 New translations en-us.json (French, Canada) 2023-12-10 15:15:55 -06:00
Hayden
62bf39f1a0 New translations en-us.json (English, United Kingdom) 2023-12-10 15:15:54 -06:00
Hayden
f7dca11b1c New translations en-us.json (Latvian) 2023-12-10 15:15:53 -06:00
Hayden
c26ffd4f46 New translations en-us.json (Croatian) 2023-12-10 15:15:52 -06:00
Hayden
c46f592d29 New translations en-us.json (Portuguese, Brazilian) 2023-12-10 15:15:51 -06:00
Hayden
5a2c2be4e6 New translations en-us.json (Galician) 2023-12-10 15:15:50 -06:00
Hayden
4c8344d91e New translations en-us.json (Vietnamese) 2023-12-10 15:15:49 -06:00
Hayden
f343c48450 New translations en-us.json (Chinese Traditional) 2023-12-10 15:15:48 -06:00
Hayden
2db654f88b New translations en-us.json (Chinese Simplified) 2023-12-10 15:15:47 -06:00
Hayden
8c9d3c1654 New translations en-us.json (Ukrainian) 2023-12-10 15:15:46 -06:00
Hayden
c62d797c38 New translations en-us.json (Turkish) 2023-12-10 15:15:45 -06:00
Hayden
f14f095d62 New translations en-us.json (Swedish) 2023-12-10 15:15:44 -06:00
Hayden
224bb5bd10 New translations en-us.json (Serbian (Cyrillic)) 2023-12-10 15:15:43 -06:00
Hayden
6dec3be096 New translations en-us.json (Slovenian) 2023-12-10 15:15:42 -06:00
Hayden
c95432dbf1 New translations en-us.json (Slovak) 2023-12-10 15:15:41 -06:00
Hayden
781722ff70 New translations en-us.json (Russian) 2023-12-10 15:15:40 -06:00
Hayden
f0cc3f7f18 New translations en-us.json (Lithuanian) 2023-12-10 15:15:39 -06:00
Hayden
8510e27176 New translations en-us.json (Korean) 2023-12-10 15:15:38 -06:00
Hayden
fd6c01414d New translations en-us.json (Italian) 2023-12-10 15:15:37 -06:00
Hayden
9908cf294d New translations en-us.json (Hungarian) 2023-12-10 15:15:35 -06:00
Hayden
a039f40a08 New translations en-us.json (Hebrew) 2023-12-10 15:15:34 -06:00
Hayden
a848d04b38 New translations en-us.json (Finnish) 2023-12-10 15:15:33 -06:00
Hayden
dfb6babf34 New translations en-us.json (Greek) 2023-12-10 15:15:32 -06:00
Hayden
e168550171 New translations en-us.json (German) 2023-12-10 15:15:31 -06:00
Hayden
b6bb341055 New translations en-us.json (Danish) 2023-12-10 15:15:30 -06:00
Hayden
d5d2893f72 New translations en-us.json (Czech) 2023-12-10 15:15:29 -06:00
Hayden
37da49fc05 New translations en-us.json (Polish) 2023-12-10 15:15:28 -06:00
Hayden
1b68aa1ee9 New translations en-us.json (Dutch) 2023-12-10 15:15:27 -06:00
Hayden
ad0f645447 New translations en-us.json (Japanese) 2023-12-10 15:15:26 -06:00
Hayden
10276a1dea New translations en-us.json (Norwegian) 2023-12-10 15:15:25 -06:00
Hayden
3823e4ab0b New translations en-us.json (Portuguese) 2023-12-10 15:15:24 -06:00
Hayden
e30614d9c8 New translations en-us.json (Bulgarian) 2023-12-10 15:15:23 -06:00
Hayden
7448583eb9 New translations en-us.json (Arabic) 2023-12-10 15:15:22 -06:00
Hayden
579d87ed56 New translations en-us.json (Afrikaans) 2023-12-10 15:15:21 -06:00
Hayden
c0da8b1282 New translations en-us.json (Spanish) 2023-12-10 15:15:19 -06:00
Hayden
59d10ccb13 New translations en-us.json (French) 2023-12-10 15:15:18 -06:00
Hayden
739213f718 New translations en-us.json (Romanian) 2023-12-10 15:15:17 -06:00
Hayden
2aed16746e New translations en-us.json (Catalan) 2023-12-10 15:15:16 -06:00
boc-the-git
7aac82bff3 fix: Add 'loading' message to settings page (#2806)
* Add 'loading' message to settings page

* Fix loading message in site settings page

* Refactor code to use AppLoader
2023-12-10 10:56:22 -06:00
Hayden
2cba2d5fd2 New Crowdin updates (#2809)
* New translations en-us.json (Polish)

* New translations en-us.json (Polish)
2023-12-09 21:22:52 +00:00
Brendan
1c1e50dbda Rearrange defineComponent to remove warnings 2023-12-09 14:41:26 +00:00
Michael Genson
f8ad72ec31 fix: Bulk URL Import Fixes (#2796)
* allow expections when fetching content

* removed extra bracket on import text

* added more fault tolerance and limited concurrency

* fix entries not being saved to report

* disable clicking into in-proress import

* conditionally render expansion
2023-12-07 17:08:47 +00:00
Kuchenpirat
449bb6f0ce fix: remove group storage capacity from profile page (#2798) 2023-12-07 06:45:52 -09:00
Hayden
415afbfae4 New translations en-us.json (Dutch) (#2797) 2023-12-06 17:33:42 -06:00
Michael Genson
b28aa82846 add logs to help debug backup issues (#2795) 2023-12-06 22:47:35 +01:00
Michael Genson
310069a7e9 fix: various alembic migration issues with queries (#2773)
* set expire_on_commit false to avoid refresh

* converted deletes to raw SQL statements

* call update statements directly in sql

* parameterized text queries

* replace orm with raw sql to avoid db differences
2023-12-06 18:37:19 +00:00
Michael Genson
1d1d61df77 fix: Missing Title and Metadata (#2770)
* add document title to server spa meta

* removed conflicting useMeta

* replaced head with useMeta

* formalized metadata injection

* small injection refactor

* added tests

* added missing global tag

* fixed setting tab title for logged-in users

* simplified metadata update

* remove duplicate tag and fix for foreign users

* add metadata for shared recipes

* added default recipe image

* fixed shared URL

---------

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2023-12-06 16:01:48 +01:00
Kuchenpirat
2751e8318a cleanup: Update makefile (#2789)
* Update makefile

* change from docker to prod
2023-12-05 08:18:21 -09:00
Hayden
b369417690 chore: restore latest tag (#2784)
* restore dockerhub publishing

* restore latest publishing tag

* fix tag/tags inputs
2023-12-04 18:58:46 +00:00
Dany Marcoux
1ac6e651d1 docs: use github.com/mealie-recipes/mealie instead of older github.com/hay-kot/mealie (#2781)
This was done with the following command:

sed -i -e 's|github\.com/hay-kot/mealie|github.com/mealie-recipes/mealie|g' docs/docs/documentation/**/*.md
2023-12-03 13:06:24 -09:00
Kuchenpirat
0e45b962b2 docs: remove from installation checklist (#2780) 2023-12-03 13:05:34 -09:00
Kuchenpirat
a130bf8847 Merge pull request #2779 from dmarcoux/installation-github-registry
Link to GitHub registry instead of DockerHub in the docs
2023-12-03 12:57:52 +01:00
Dany Marcoux
84b2519320 Link to GitHub registry instead of DockerHub in the docs 2023-12-03 12:39:51 +01:00
Kuchenpirat
f32444b91d fix: handle Recipe Times as dicts and lists (#2764)
* handle dicts

* 🧹

* handle arrays

* change default case & add warning logger

* lint

* typo

* update dict case

* update list case

* add timedelta to cases

* remove timedelta so mypy is happy
2023-12-03 05:15:44 +00:00
boc-the-git
a2d9387077 Merge pull request #2778 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-03 07:04:32 +11:00
Hayden
27c4d92517 New translations en-us.json (Catalan) 2023-12-02 13:59:19 -06:00
boc-the-git
0135a4e73e Merge pull request #2777 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-12-02 07:21:08 +11:00
Hayden
c1393a6b0c New translations en-us.json (Japanese) 2023-12-01 13:59:17 -06:00
Hayden
5639047167 New translations en-us.json (Japanese) 2023-12-01 13:59:16 -06:00
Hayden
79b4342690 New translations en-us.json (Japanese) 2023-12-01 13:59:15 -06:00
Hayden
a5fecbc48a New translations en-us.json (Norwegian) (#2776) 2023-11-30 14:00:39 -06:00
Hayden
46cae07f2c New Crowdin updates (#2774)
* New translations en-us.json (Catalan)

* New translations en-us.json (Portuguese)
2023-11-29 13:44:31 -06:00
Jack Bailey
6e2c30aba5 Merge branch 'mealie-next' into mealie-next 2023-11-29 16:56:27 +00:00
Jack Bailey
9c01b72292 Remove unused code 2023-11-29 10:16:25 +00:00
boc-the-git
5ee077e09f Merge pull request #2767 from mealie-recipes/l10n_mealie-next
New Crowdin updates
2023-11-28 21:27:05 +11:00
Hayden
a5d7787fd0 New translations en-us.json (Danish) 2023-11-27 13:32:46 -06:00
boc-the-git
18a405808a Merge branch 'mealie-next' into mealie-next 2023-11-25 21:07:01 +11:00
boc-the-git
01e20acce7 Merge branch 'mealie-next' into mealie-next 2023-11-25 07:44:26 +11:00
Jack Bailey
1ee29e9e45 Revert other language files 2023-11-23 13:46:37 +00:00
Jack Bailey
8a2d640922 Merge branch 'mealie-next' into mealie-next 2023-11-23 10:47:25 +00:00
Jack Bailey
8170e66f4f Merge branch 'mealie-next' into mealie-next 2023-11-15 08:59:35 +00:00
Jack Bailey
e50788f685 Remove leftover debug variable 2023-11-13 09:40:10 +00:00
Jack Bailey
1197aa3f37 Add functionality to edit mealplan item 2023-11-13 02:20:08 +00:00
235 changed files with 7451 additions and 6267 deletions

View File

@@ -14,6 +14,7 @@ RUN echo "export PROMPT_COMMAND='history -a'" >> /home/vscode/.bashrc \
&& echo "export HISTFILE=~/commandhistory/.bash_history" >> /home/vscode/.bashrc \
&& chown vscode:vscode -R /home/vscode/
RUN npm install -g @go-task/cli
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
@@ -37,4 +38,3 @@ RUN apt-get update \
libwebp-dev \
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1
# && pip install -U --no-cache-dir pip

View File

@@ -41,11 +41,12 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
3000,
9000
9000,
24678 // used by nuxt when hot-reloading using polling
],
// Use 'onCreateCommand' to run commands at the end of container creation.
// Use 'postCreateCommand' to run commands after the container is created.
"onCreateCommand": "sudo chown -R vscode:vscode /workspaces/mealie/frontend/node_modules && make setup",
"onCreateCommand": "sudo chown -R vscode:vscode /workspaces/mealie/frontend/node_modules && task setup",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
// "features": {

View File

@@ -1,7 +1,7 @@
---
name: Bug Report
description: "Submit a bug for the latest version of Mealie"
title: "[BUG] - YOUR TITLE"
title: "[BUG] - YOUR DESCRIPTIVE TITLE GOES HERE"
labels: ["bug", "triage"]
body:
- type: checkboxes
@@ -14,7 +14,7 @@ body:
options:
- label: This is not a feature request.
required: true
- label: I added a very descriptive title to this issue.
- label: I added a very descriptive title to this issue (title field is above this).
required: true
- label: I used the GitHub search to find a similar issue and didn't find it.
required: true
@@ -42,7 +42,7 @@ body:
id: logs
attributes:
label: Please provide relevant logs
placeholder: For example from `docker-compose logs` or other system logs.
placeholder: For example from `docker compose logs` or other system logs.
validations:
required: true
- type: textarea
@@ -61,7 +61,8 @@ body:
- Docker (Windows)
- Docker (Synology)
- Unraid
- Other
- TrueNAS
- Other (please specify below)
validations:
required: true
- type: textarea

21
.github/stale.yml vendored
View File

@@ -1,21 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- early-stages
- "bug: confirmed"
- feedback
- task
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

81
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "mealie-next" ]
pull_request:
branches: [ "mealie-next" ]
schedule:
- cron: '36 9 * * 3'
jobs:
analyze:
name: Analyze
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners
# Consider using larger runners for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript-typescript', 'python' ]
# CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]
# Use only 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@@ -35,8 +35,14 @@ jobs:
# Steps
steps:
- name: Install Task
uses: arduino/setup-task@v1
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Check out repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up python
uses: actions/setup-python@v4
@@ -60,14 +66,14 @@ jobs:
id: cache-validate
if: steps.cached-poetry-dependencies.outputs.cache-hit == 'true'
run: |
echo "import black;print('venv good?')" > test.py && poetry run python test.py && echo ::set-output name=cache-hit-success::true
echo "import black;print('venv good?')" > test.py && poetry run python test.py && echo "cache-hit-success=true" >> $GITHUB_OUTPUT
rm test.py
continue-on-error: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev tesseract-ocr-all
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev
poetry install
poetry add "psycopg2-binary==2.8.6"
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' || steps.cache-validate.outputs.cache-hit-success != 'true'
@@ -78,11 +84,11 @@ jobs:
- name: Lint (Ruff)
run: |
make backend-lint
task py:lint
- name: Mypy Typecheck
run: |
make backend-typecheck
task py:mypy
- name: Pytest
env:
@@ -101,4 +107,4 @@ jobs:
LDAP_NAME_ATTRIBUTE: cn
LDAP_MAIL_ATTRIBUTE: mail
run: |
make backend-test
task py:test

View File

@@ -6,6 +6,9 @@ on:
tag:
required: true
type: string
tags:
required: false
type: string
secrets:
DOCKERHUB_USERNAME:
required: true
@@ -17,33 +20,42 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v2
- name: Log in to the Container registry (ghcr.io)
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to the Container registry (dockerhub)
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Override __init__.py
run: |
echo "__version__ = \"${{ inputs.tag }}\"" > ./mealie/__init__.py
- name: Build and push Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
file: ./docker/Dockerfile
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ghcr.io/${{ github.repository }}:${{ inputs.tag }}
tags: |
hkotel/mealie:${{ inputs.tag }}
ghcr.io/${{ github.repository }}:${{ inputs.tag }}
${{ inputs.tags }}
build-args: |
COMMIT=${{ github.sha }}
# https://docs.docker.com/build/ci/github-actions/cache/#github-cache

View File

@@ -9,20 +9,20 @@ jobs:
steps:
- name: Checkout 🛎
uses: actions/checkout@master
uses: actions/checkout@v4
- name: Setup node env 🏗
uses: actions/setup-node@v3.7.0
uses: actions/setup-node@v4.0.0
with:
node-version: 16
check-latest: true
- name: Get yarn cache directory path 🛠
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Cache node_modules 📦
uses: actions/cache@v3.3.1
uses: actions/cache@v3.3.2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@@ -47,20 +47,20 @@ jobs:
steps:
- name: Checkout 🛎
uses: actions/checkout@master
uses: actions/checkout@v4
- name: Setup node env 🏗
uses: actions/setup-node@v3.7.0
uses: actions/setup-node@v4.0.0
with:
node-version: 16
check-latest: true
- name: Get yarn cache directory path 🛠
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Cache node_modules 📦
uses: actions/cache@v3.3.1
uses: actions/cache@v3.3.2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}

View File

@@ -11,7 +11,7 @@ jobs:
fail-fast: true
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Build Dockerfile
run: |

View File

@@ -24,6 +24,9 @@ jobs:
- frontend-tests
with:
tag: ${{ github.event.release.tag_name }}
tags: |
hkotel/mealie:latest
ghcr.io/${{ github.repository }}:latest
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}

41
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
# Documentation: https://github.com/marketplace/actions/close-stale-issues
name: 'Stale: Flag and close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-issue-label: 'stale'
exempt-issue-labels: 'pinned,security,early-stages,bug: confirmed,feedback,task'
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
days-before-issue-stale: 30
days-before-issue-close: 5
stale-pr-label: 'stale'
stale-pr-message: 'This PR is stale because it has been open 45 days with no activity.'
days-before-pr-stale: 45
# This stops a PR from ever getting closed automatically.
days-before-pr-close: -1
# If an issue/PR has a milestone, it's exempt from being marked as stale.
exempt-all-milestones: true
# How many API calls will we allow the action to make, essentially.
# Doco: https://github.com/actions/stale?tab=readme-ov-file#operations-per-run
operations-per-run: 150
########################################################################
# The below are just default values, but populating here for reference #
########################################################################
# Automatically remove the stale label when the issues or the pull requests are updated
remove-stale-when-updated: true
# The reason used when closing issues. Valid values are `completed` and `not_planned`.
close-issue-reason: 'not_planned'
# If true, PRs currently in draft will not be marked as stale automatically.
# We can mark them stale (after `days-before-pr-stale`), though we don't auto-close.
exempt-draft-pr: false

1
.gitignore vendored
View File

@@ -162,3 +162,4 @@ lcov.info
dev/code-generation/openapi.json
.run/
.task/*

View File

@@ -60,17 +60,7 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=print-statement,
parameter-unpacking,
unpacking-in-except,
old-raise-syntax,
backtick,
long-suffix,
old-ne-operator,
old-octal-literal,
import-star-module-level,
non-ascii-bytes-literal,
raw-checker-failed,
disable=raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
@@ -78,67 +68,10 @@ disable=print-statement,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
apply-builtin,
basestring-builtin,
buffer-builtin,
cmp-builtin,
coerce-builtin,
execfile-builtin,
file-builtin,
long-builtin,
raw_input-builtin,
reduce-builtin,
standarderror-builtin,
unicode-builtin,
xrange-builtin,
coerce-method,
delslice-method,
getslice-method,
setslice-method,
no-absolute-import,
old-division,
dict-iter-method,
dict-view-method,
next-method-called,
metaclass-assignment,
indexing-exception,
raising-string,
reload-builtin,
oct-method,
hex-method,
nonzero-method,
cmp-method,
input-builtin,
round-builtin,
intern-builtin,
unichr-builtin,
map-builtin-not-iterating,
zip-builtin-not-iterating,
range-builtin-not-iterating,
filter-builtin-not-iterating,
using-cmp-argument,
eq-without-hash,
div-method,
idiv-method,
rdiv-method,
exception-message-attribute,
invalid-str-codec,
sys-max-int,
bad-python3-import,
deprecated-string-function,
deprecated-str-translate-call,
deprecated-itertools-function,
deprecated-types-field,
next-method-defined,
dict-items-not-iterating,
dict-keys-not-iterating,
dict-values-not-iterating,
deprecated-operator-function,
deprecated-urllib-function,
xreadlines-attribute,
deprecated-sys-function,
exception-escape,
comprehension-escape
missing-function-docstring,
missing-class-docstring,
missing-module-docstring,
too-few-public-methods
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
@@ -292,7 +225,9 @@ good-names=i,
k,
ex,
Run,
_
_,
e,
db
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
@@ -455,7 +390,7 @@ indent-after-paren=4
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
max-line-length=120
# Maximum number of lines in a module.
max-module-lines=1000
@@ -583,5 +518,5 @@ valid-metaclass-classmethod-first-arg=cls
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception
overgeneral-exceptions=builtins.BaseException,
builtins.Exception

View File

@@ -14,8 +14,8 @@
"webp"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": false
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"editor.formatOnSave": true,
"eslint.workingDirectories": [

23
.vscode/tasks.json vendored
View File

@@ -1,22 +1,9 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "DEV: Build and Start Docker Compose",
"command": "make docker-dev",
"type": "shell",
"args": [],
"problemMatcher": [
"$tsc"
],
"presentation": {
"reveal": "always"
},
"group": "test"
},
{
"label": "Production: Build and Start Docker Compose",
"command": "make docker-prod",
"command": "task docker:prod",
"type": "shell",
"args": [],
"problemMatcher": [
@@ -29,7 +16,7 @@
},
{
"label": "Dev: Start Backend",
"command": "make backend",
"command": "task py",
"type": "shell",
"presentation": {
"reveal": "always",
@@ -49,7 +36,7 @@
},
{
"label": "Dev: Start Frontend",
"command": "make frontend",
"command": "task ui",
"type": "shell",
"presentation": {
"reveal": "always",
@@ -59,7 +46,7 @@
},
{
"label": "Dev: Start Docs Server",
"command": "make docs",
"command": "task docs",
"type": "shell",
"presentation": {
"reveal": "always",
@@ -69,7 +56,7 @@
},
{
"label": "Run python tests",
"command": "make test",
"command": "task py:test",
"type": "shell",
"presentation": {
"reveal": "always"

View File

@@ -1,15 +1,15 @@
[![Latest Release][latest-release-shield]][latest-release-url]
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![AGPL License][license-shield]][license-url]
[![Docker Pulls][docker-pull]][docker-pull]
[![CodeFactor](https://www.codefactor.io/repository/github/mealie-recipes/mealie/badge)](https://www.codefactor.io/repository/github/mealie-recipes/mealie)
<!-- PROJECT LOGO -->
<br />
<p align="center">
<a href="https://github.com/hay-kot/mealie">
<a href="https://github.com/mealie-recipes/mealie">
<svg style="width:100px;height:100px" viewBox="0 0 24 24">
<path fill="currentColor" d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z" />
</svg>
@@ -21,12 +21,12 @@
A Place for All Your Recipes
<br />
<a href="https://nightly.mealie.io"><strong>Explore the docs »</strong></a>
<a href="https://github.com/hay-kot/mealie">
<a href="https://github.com/mealie-recipes/mealie">
</a>
<br />
<a href="https://demo.mealie.io/">View Demo</a>
·
<a href="https://github.com/hay-kot/mealie/issues">Report Bug</a>
<a href="https://github.com/mealie-recipes/mealie/issues">Report Bug</a>
·
<a href="https://github.com/mealie-recipes/mealie/pkgs/container/mealie">GitHub Container Registry</a>
</p>
@@ -56,6 +56,12 @@ If you are not a coder, you can still contribute financially. Financial contribu
<a href="https://www.buymeacoffee.com/haykot" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 107px !important;" ></a>
### Translations
Translations can be a great way for **non-coders** to contribute to project. We use [Crowdin](https://crowdin.com/project/mealie) to allow several contributors to work on translating Mealie. You can simply help by voting for your preferred translations, or even by completely translating Mealie into a new language.
For more information, check out the translation page on the [contributor's guide](https://nightly.mealie.io/contributors/translating/).
<!-- LICENSE -->
## License
Distributed under the AGPL License. See `LICENSE` for more information.
@@ -76,17 +82,19 @@ Thanks to Linode for providing Hosting for the Demo, Beta, and Documentation sit
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/hay-kot/mealie.svg?style=flat-square
[contributors-shield]: https://img.shields.io/github/contributors/mealie-recipes/mealie.svg?style=flat-square
[docker-pull]: https://img.shields.io/docker/pulls/hkotel/mealie
[contributors-url]: https://github.com/hay-kot/mealie/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/hay-kot/mealie.svg?style=flat-square
[forks-url]: https://github.com/hay-kot/mealie/network/members
[stars-shield]: https://img.shields.io/github/stars/hay-kot/mealie.svg?style=flat-square
[stars-url]: https://github.com/hay-kot/mealie/stargazers
[issues-shield]: https://img.shields.io/github/issues/hay-kot/mealie.svg?style=flat-square
[issues-url]: https://github.com/hay-kot/mealie/issues
[license-shield]: https://img.shields.io/github/license/hay-kot/mealie.svg?style=flat-square
[license-url]: https://github.com/hay-kot/mealie/blob/mealie-next/LICENSE
[contributors-url]: https://github.com/mealie-recipes/mealie/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/mealie-recipes/mealie.svg?style=flat-square
[forks-url]: https://github.com/mealie-recipes/mealie/network/members
[stars-shield]: https://img.shields.io/github/stars/mealie-recipes/mealie.svg?style=flat-square
[stars-url]: https://github.com/mealie-recipes/mealie/stargazers
[issues-shield]: https://img.shields.io/github/issues/mealie-recipes/mealie.svg?style=flat-square
[issues-url]: https://github.com/mealie-recipes/mealie/issues
[latest-release-shield]: https://img.shields.io/github/v/release/mealie-recipes/mealie.svg?style=flat-square
[latest-release-url]: https://img.shields.io/github/v/release/mealie-recipes/mealie
[license-shield]: https://img.shields.io/github/license/mealie-recipes/mealie.svg?style=flat-square
[license-url]: https://github.com/mealie-recipes/mealie/blob/mealie-next/LICENSE
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
[linkedin-url]: https://linkedin.com/in/hay-kot
[product-screenshot]: docs/docs/assets/img/home_screenshot.png

183
Taskfile.yml Normal file
View File

@@ -0,0 +1,183 @@
# https://taskfile.dev
version: "3"
vars:
GREETING: Hello, World!
env:
DEFAULT_GROUP: Home
PRODUCTION: false
API_PORT: 9000
API_DOCS: True
TOKEN_TIME: 256 # hours
# mailplit SMTP config
# start dev:services to use mailpit
SMTP_HOST: localhost
SMTP_PORT: 1025
SMTP_FROM_NAME: MealieDev
SMTP_AUTH_STRATEGY: NONE
LANG: en-US
# loads .env file if it exists
dotenv:
- .env
- .dev.env
tasks:
docs:gen:
desc: runs the API documentation generator
cmds:
- poetry run python dev/code-generation/gen_docs_api.py
docs:
desc: runs the documentation server
dir: docs
deps:
- docs:gen
cmds:
- poetry run python -m mkdocs serve
setup:ui:
desc: setup frontend dependencies
dir: frontend
cmds:
- yarn install
setup:py:
desc: setup python dependencies
cmds:
- poetry install --with main,dev,postgres
- poetry run pre-commit install
setup:model:
desc: setup nlp model
vars:
MODEL_URL: https://github.com/mealie-recipes/nlp-model/releases/download/v1.0.0/model.crfmodel
OUTPUT: ./mealie/services/parser_services/crfpp/model.crfmodel
sources:
# using pyproject.toml as the dependency since this should only ever need to run once
# during setup. There is perhaps a better way to do this.
- ./pyproject.toml
generates:
- ./mealie/services/parser_services/crfpp/model.crfmodel
cmds:
- curl -L0 {{ .MODEL_URL }} --output {{ .OUTPUT }}
setup:
desc: setup all dependencies
deps:
- setup:ui
- setup:py
- setup:model
dev:generate:
desc: run code generators
cmds:
- poetry run python dev/code-generation/main.py
dev:services:
desc: starts postgres and mailpit containers
dir: docker
cmds:
- docker compose -f docker-compose.dev.yml up
dev:clean:
desc: cleans up dev environment !! removes all data files !!
vars:
DEV_DATA: ""
cmds:
- rm -r ./dev/data/recipes/
- rm -r ./dev/data/users/
- rm -f ./dev/data/mealie*.db
- rm -f ./dev/data/mealie.log
- rm -f ./dev/data/.secret
py:mypy:
desc: runs python type checking
cmds:
- poetry run mypy mealie
py:test:
desc: runs python tests (support args after '--')
cmds:
- poetry run pytest {{ .CLI_ARGS }}
py:format:
desc: runs python code formatter
cmds:
- poetry run black mealie
py:lint:
desc: runs python linter
cmds:
- poetry run ruff mealie
py:check:
desc: runs all linters, type checkers, and formatters
deps:
- py:format
- py:lint
- py:mypy
- py:test
py:coverage:
desc: runs python coverage and generates html report
cmds:
- poetry run pytest
- poetry run coverage report -m
- poetry run coveragepy-lcov
- poetry run coverage html
- open htmlcov/index.html
py:
desc: runs the backend server
cmds:
- poetry run python mealie/db/init_db.py
- poetry run python mealie/app.py
py:postgres:
desc: runs the backend server configured for containerized postgres
env:
DB_ENGINE: postgres
POSTGRES_USER: mealie
POSTGRES_PASSWORD: mealie
POSTGRES_SERVER: localhost
POSTGRES_PORT: 5432
POSTGRES_DB: mealie
cmds:
- poetry run python mealie/db/init_db.py
- poetry run python mealie/app.py
ui:build:
desc: builds the frontend in frontend/dist
dir: frontend
cmds:
- yarn build
ui:lint:
desc: runs the frontend linter
dir: frontend
cmds:
- yarn lint
ui:test:
desc: runs the frontend tests
dir: frontend
cmds:
- yarn test
ui:check:
desc: runs all frontend checks
deps:
- ui:lint
- ui:test
ui:
desc: runs the frontend server
dir: frontend
cmds:
- yarn run dev
docker:prod:
desc: builds and runs the production docker image locally
dir: docker
cmds:
- docker compose -f docker-compose.yml -p mealie up -d --build

View File

@@ -7,12 +7,11 @@ Create Date: 2023-02-14 20:45:41.102571
"""
import sqlalchemy as sa
from sqlalchemy import orm, select
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from text_unidecode import unidecode
import mealie.db.migration_types
from alembic import op
from mealie.db.models._model_utils import GUID
# revision identifiers, used by Alembic.
@@ -52,30 +51,46 @@ def do_data_migration():
session = orm.Session(bind=bind)
recipes = session.execute(select(RecipeModel)).scalars().all()
ingredients = session.execute(select(RecipeIngredient)).scalars().all()
for recipe in recipes:
if recipe.name is not None:
recipe.name_normalized = unidecode(recipe.name).lower().strip()
session.execute(
sa.text(
f"UPDATE {RecipeModel.__tablename__} SET name_normalized=:name_normalized WHERE id=:id"
).bindparams(name_normalized=unidecode(recipe.name).lower().strip(), id=recipe.id)
)
if recipe.description is not None:
recipe.description_normalized = unidecode(recipe.description).lower().strip()
session.add(recipe)
session.execute(
sa.text(
f"UPDATE {RecipeModel.__tablename__} SET description_normalized=:description_normalized WHERE id=:id"
).bindparams(description_normalized=unidecode(recipe.description).lower().strip(), id=recipe.id)
)
ingredients = session.execute(select(RecipeIngredient)).scalars().all()
for ingredient in ingredients:
if ingredient.note is not None:
ingredient.note_normalized = unidecode(ingredient.note).lower().strip()
session.execute(
sa.text(
f"UPDATE {RecipeIngredient.__tablename__} SET note_normalized=:note_normalized WHERE id=:id"
).bindparams(note_normalized=unidecode(ingredient.note).lower().strip(), id=ingredient.id)
)
if ingredient.original_text is not None:
ingredient.original_text_normalized = unidecode(ingredient.original_text).lower().strip()
session.add(ingredient)
session.execute(
sa.text(
f"UPDATE {RecipeIngredient.__tablename__} SET original_text_normalized=:original_text_normalized WHERE id=:id"
).bindparams(
original_text_normalized=unidecode(ingredient.original_text).lower().strip(), id=ingredient.id
)
)
session.commit()
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# Set column to nullable first, since we do not have values here yet
op.add_column("recipes", sa.Column("name_normalized", sa.String(), nullable=True))
# Set column default first, since we do not have values here yet
op.add_column("recipes", sa.Column("name_normalized", sa.String(), nullable=False, server_default=""))
op.add_column("recipes", sa.Column("description_normalized", sa.String(), nullable=True))
op.drop_index("ix_recipes_description", table_name="recipes")
op.drop_index("ix_recipes_name", table_name="recipes")
@@ -95,9 +110,9 @@ def upgrade():
unique=False,
)
do_data_migration()
# Make recipes.name_normalized not nullable now that column should be filled for all rows
# Remove server default now that column should be filled for all rows
with op.batch_alter_table("recipes", schema=None) as batch_op:
batch_op.alter_column("name_normalized", nullable=False, existing_type=sa.String())
batch_op.alter_column("name_normalized", existing_type=sa.String(), server_default=None)
# ### end Alembic commands ###

View File

@@ -24,10 +24,10 @@ depends_on = None
def populate_shopping_lists_multi_purpose_labels(shopping_lists_multi_purpose_labels_table: sa.Table, session: Session):
shopping_lists = session.query(ShoppingList).all()
labels = session.query(MultiPurposeLabel).all()
shopping_lists_labels_data: list[dict] = []
for shopping_list in shopping_lists:
labels = session.query(MultiPurposeLabel).filter(MultiPurposeLabel.group_id == ShoppingList.group_id).all()
for i, label in enumerate(labels):
shopping_lists_labels_data.append(
{"id": uuid4(), "shopping_list_id": shopping_list.id, "label_id": label.id, "position": i}

View File

@@ -24,17 +24,22 @@ def populate_group_slugs(session: Session):
seen_slugs: set[str] = set()
for group in groups:
original_name = group.name
new_name = original_name
attempts = 0
while True:
slug = slugify(group.name)
slug = slugify(new_name)
if slug not in seen_slugs:
break
attempts += 1
group.name = f"{original_name} ({attempts})"
new_name = f"{original_name} ({attempts})"
seen_slugs.add(slug)
group.slug = slug
session.execute(
sa.text(f"UPDATE {Group.__tablename__} SET name=:name, slug=:slug WHERE id=:id").bindparams(
name=new_name, slug=slug, id=group.id
)
)
session.commit()

View File

@@ -33,21 +33,29 @@ def populate_normalized_fields():
)
for unit in units:
if unit.name is not None:
unit.name_normalized = IngredientUnitModel.normalize(unit.name)
session.execute(
sa.text(
f"UPDATE {IngredientUnitModel.__tablename__} SET name_normalized=:name_normalized WHERE id=:id"
).bindparams(name_normalized=IngredientUnitModel.normalize(unit.name), id=unit.id)
)
if unit.abbreviation is not None:
unit.abbreviation_normalized = IngredientUnitModel.normalize(unit.abbreviation)
session.add(unit)
session.execute(
sa.text(
f"UPDATE {IngredientUnitModel.__tablename__} SET abbreviation_normalized=:abbreviation_normalized WHERE id=:id"
).bindparams(abbreviation_normalized=IngredientUnitModel.normalize(unit.abbreviation), id=unit.id)
)
foods = (
session.execute(select(IngredientFoodModel).options(orm.load_only(IngredientFoodModel.name))).scalars().all()
)
for food in foods:
if food.name is not None:
food.name_normalized = IngredientFoodModel.normalize(food.name)
session.add(food)
session.execute(
sa.text(
f"UPDATE {IngredientFoodModel.__tablename__} SET name_normalized=:name_normalized WHERE id=:id"
).bindparams(name_normalized=IngredientFoodModel.normalize(food.name), id=food.id)
)
session.commit()

View File

@@ -13,10 +13,8 @@ import sqlalchemy as sa
from pydantic import UUID4
from sqlalchemy.orm import Session, load_only
import mealie.db.migration_types
from alembic import op
from mealie.db.models._model_base import SqlAlchemyBase
from mealie.db.models._model_utils.guid import GUID
from mealie.db.models.group.shopping_list import ShoppingListItem
from mealie.db.models.labels import MultiPurposeLabel
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel, RecipeIngredientModel
@@ -43,26 +41,25 @@ def _is_postgres():
return op.get_context().dialect.name == "postgresql"
def _get_duplicates(session: Session, model: SqlAlchemyBase) -> defaultdict[str, list[str]]:
duplicate_map: defaultdict[str, list[str]] = defaultdict(list)
for obj in session.query(model).options(load_only(model.id, model.group_id, model.name)).all():
key = f"{obj.group_id}$${obj.name}"
duplicate_map[key].append(str(obj.id))
def _get_duplicates(session: Session, model: SqlAlchemyBase) -> defaultdict[str, list]:
duplicate_map: defaultdict[str, list] = defaultdict(list)
query = session.execute(sa.text(f"SELECT id, group_id, name FROM {model.__tablename__}"))
for row in query.all():
id, group_id, name = row
key = f"{group_id}$${name}"
duplicate_map[key].append(id)
return duplicate_map
def _resolve_duplicate_food(
session: Session,
keep_food: IngredientFoodModel,
keep_food_id: UUID4,
dupe_food_id: UUID4,
):
for shopping_list_item in session.query(ShoppingListItem).filter_by(food_id=dupe_food_id).all():
shopping_list_item.food_id = keep_food_id
shopping_list_item.food = keep_food
session.commit()
for recipe_ingredient in (
session.query(RecipeIngredientModel)
@@ -71,62 +68,49 @@ def _resolve_duplicate_food(
.all()
):
recipe_ingredient.food_id = keep_food_id
recipe_ingredient.food = keep_food
session.commit()
session.query(IngredientFoodModel).options(load_only(IngredientFoodModel.id)).filter_by(id=dupe_food_id).delete()
session.execute(
sa.text(f"DELETE FROM {IngredientFoodModel.__tablename__} WHERE id=:id").bindparams(id=dupe_food_id)
)
session.commit()
def _resolve_duplicate_unit(
session: Session,
keep_unit: IngredientUnitModel,
keep_unit_id: UUID4,
dupe_unit_id: UUID4,
):
for shopping_list_item in session.query(ShoppingListItem).filter_by(unit_id=dupe_unit_id).all():
shopping_list_item.unit_id = keep_unit_id
shopping_list_item.unit = keep_unit
session.commit()
for recipe_ingredient in session.query(RecipeIngredientModel).filter_by(unit_id=dupe_unit_id).all():
recipe_ingredient.unit_id = keep_unit_id
recipe_ingredient.unit = keep_unit
session.commit()
session.query(IngredientUnitModel).options(load_only(IngredientUnitModel.id)).filter_by(id=dupe_unit_id).delete()
session.execute(
sa.text(f"DELETE FROM {IngredientUnitModel.__tablename__} WHERE id=:id").bindparams(id=dupe_unit_id)
)
session.commit()
def _resolve_duplicate_label(
session: Session,
keep_label: MultiPurposeLabel,
keep_label_id: UUID4,
dupe_label_id: UUID4,
):
for shopping_list_item in session.query(ShoppingListItem).filter_by(label_id=dupe_label_id).all():
shopping_list_item.label_id = keep_label_id
shopping_list_item.label = keep_label
session.commit()
for ingredient_food in session.query(IngredientFoodModel).filter_by(label_id=dupe_label_id).all():
ingredient_food.label_id = keep_label_id
ingredient_food.label = keep_label
session.commit()
session.query(MultiPurposeLabel).options(load_only(MultiPurposeLabel.id)).filter_by(id=dupe_label_id).delete()
session.execute(sa.text(f"DELETE FROM {MultiPurposeLabel.__tablename__} WHERE id=:id").bindparams(id=dupe_label_id))
session.commit()
def _resolve_duplicate_foods_units_labels():
bind = op.get_bind()
session = Session(bind=bind)
def _resolve_duplicate_foods_units_labels(session: Session):
for model, resolve_func in [
(IngredientFoodModel, _resolve_duplicate_food),
(IngredientUnitModel, _resolve_duplicate_unit),
@@ -138,9 +122,8 @@ def _resolve_duplicate_foods_units_labels():
continue
keep_id = ids[0]
keep_obj = session.query(model).options(load_only(model.id)).filter_by(id=keep_id).first()
for dupe_id in ids[1:]:
resolve_func(session, keep_obj, keep_id, dupe_id)
resolve_func(session, keep_id, dupe_id)
def _remove_duplicates_from_m2m_table(session: Session, table_meta: TableMeta):
@@ -166,17 +149,18 @@ def _remove_duplicates_from_m2m_table(session: Session, table_meta: TableMeta):
session.commit()
def _remove_duplicates_from_m2m_tables(table_metas: list[TableMeta]):
bind = op.get_bind()
session = Session(bind=bind)
def _remove_duplicates_from_m2m_tables(session: Session, table_metas: list[TableMeta]):
for table_meta in table_metas:
_remove_duplicates_from_m2m_table(session, table_meta)
def upgrade():
_resolve_duplicate_foods_units_labels()
bind = op.get_bind()
session = Session(bind=bind)
_resolve_duplicate_foods_units_labels(session)
_remove_duplicates_from_m2m_tables(
session,
[
TableMeta("cookbooks_to_categories", "cookbook_id", "category_id"),
TableMeta("cookbooks_to_tags", "cookbook_id", "tag_id"),
@@ -189,12 +173,13 @@ def upgrade():
TableMeta("recipes_to_tools", "recipe_id", "tool_id"),
TableMeta("users_to_favorites", "user_id", "recipe_id"),
TableMeta("shopping_lists_multi_purpose_labels", "shopping_list_id", "label_id"),
]
],
)
session.commit()
# ### commands auto generated by Alembic - please adjust! ###
# we use batch_alter_table here because otherwise this fails on sqlite
# M2M
with op.batch_alter_table("cookbooks_to_categories") as batch_op:
batch_op.create_unique_constraint("cookbook_id_category_id_key", ["cookbook_id", "category_id"])

View File

@@ -1,4 +1,5 @@
import pathlib
from dataclasses import dataclass
from pathlib import Path
import dotenv
@@ -15,38 +16,44 @@ BASE = pathlib.Path(__file__).parent.parent.parent
API_KEY = dotenv.get_key(BASE / ".env", "CROWDIN_API_KEY")
NAMES = {
"en-US": "American English",
"en-GB": "British English",
"af-ZA": "Afrikaans (Afrikaans)",
"ar-SA": "العربية (Arabic)",
"ca-ES": "Català (Catalan)",
"cs-CZ": "Čeština (Czech)",
"da-DK": "Dansk (Danish)",
"de-DE": "Deutsch (German)",
"el-GR": "Ελληνικά (Greek)",
"es-ES": "Español (Spanish)",
"fi-FI": "Suomi (Finnish)",
"fr-FR": "Français (French)",
"he-IL": "עברית (Hebrew)",
"hu-HU": "Magyar (Hungarian)",
"it-IT": "Italiano (Italian)",
"ja-JP": "日本語 (Japanese)",
"ko-KR": "한국어 (Korean)",
"no-NO": "Norsk (Norwegian)",
"nl-NL": "Nederlands (Dutch)",
"pl-PL": "Polski (Polish)",
"pt-BR": "Português do Brasil (Brazilian Portuguese)",
"pt-PT": "Português (Portuguese)",
"ro-RO": "Română (Romanian)",
"ru-RU": "Pусский (Russian)",
"sr-SP": "српски (Serbian)",
"sv-SE": "Svenska (Swedish)",
"tr-TR": "Türkçe (Turkish)",
"uk-UA": "Українська (Ukrainian)",
"vi-VN": "Tiếng Việt (Vietnamese)",
"zh-CN": "简体中文 (Chinese simplified)",
"zh-TW": "繁體中文 (Chinese traditional)",
@dataclass
class LocaleData:
name: str
dir: str = "ltr"
LOCALE_DATA: dict[str, LocaleData] = {
"en-US": LocaleData(name="American English"),
"en-GB": LocaleData(name="British English"),
"af-ZA": LocaleData(name="Afrikaans (Afrikaans)"),
"ar-SA": LocaleData(name="العربية (Arabic)", dir="rtl"),
"ca-ES": LocaleData(name="Català (Catalan)"),
"cs-CZ": LocaleData(name="Čeština (Czech)"),
"da-DK": LocaleData(name="Dansk (Danish)"),
"de-DE": LocaleData(name="Deutsch (German)"),
"el-GR": LocaleData(name="Ελληνικά (Greek)"),
"es-ES": LocaleData(name="Español (Spanish)"),
"fi-FI": LocaleData(name="Suomi (Finnish)"),
"fr-FR": LocaleData(name="Français (French)"),
"he-IL": LocaleData(name="עברית (Hebrew)", dir="rtl"),
"hu-HU": LocaleData(name="Magyar (Hungarian)"),
"it-IT": LocaleData(name="Italiano (Italian)"),
"ja-JP": LocaleData(name="日本語 (Japanese)"),
"ko-KR": LocaleData(name="한국어 (Korean)"),
"no-NO": LocaleData(name="Norsk (Norwegian)"),
"nl-NL": LocaleData(name="Nederlands (Dutch)"),
"pl-PL": LocaleData(name="Polski (Polish)"),
"pt-BR": LocaleData(name="Português do Brasil (Brazilian Portuguese)"),
"pt-PT": LocaleData(name="Português (Portuguese)"),
"ro-RO": LocaleData(name="Română (Romanian)"),
"ru-RU": LocaleData(name="Pусский (Russian)"),
"sr-SP": LocaleData(name="српски (Serbian)"),
"sv-SE": LocaleData(name="Svenska (Swedish)"),
"tr-TR": LocaleData(name="Türkçe (Turkish)"),
"uk-UA": LocaleData(name="Українська (Ukrainian)"),
"vi-VN": LocaleData(name="Tiếng Việt (Vietnamese)"),
"zh-CN": LocaleData(name="简体中文 (Chinese simplified)"),
"zh-TW": LocaleData(name="繁體中文 (Chinese traditional)"),
}
LOCALE_TEMPLATE = """// This Code is auto generated by gen_global_components.py
@@ -55,6 +62,7 @@ export const LOCALES = [{% for locale in locales %}
name: "{{ locale.name }}",
value: "{{ locale.locale }}",
progress: {{ locale.progress }},
dir: "{{ locale.dir }}",
},{% endfor %}
]
@@ -65,12 +73,14 @@ class TargetLanguage(MealieModel):
id: str
name: str
locale: str
dir: str = "ltr"
threeLettersCode: str
twoLettersCode: str
progress: float = 0.0
class Config:
extra = Extra.allow
allow_population_by_field_name = True
class CrowdinApi:
@@ -103,15 +113,23 @@ class CrowdinApi:
models.insert(
0,
TargetLanguage(
id="en-US", name="English", locale="en-US", threeLettersCode="en", twoLettersCode="en", progress=100
id="en-US",
name="English",
locale="en-US",
dir="ltr",
threeLettersCode="en",
twoLettersCode="en",
progress=100,
),
)
progress: list[dict] = self.get_progress()["data"]
for model in models:
if model.locale in NAMES:
model.name = NAMES[model.locale]
if model.locale in LOCALE_DATA:
locale_data = LOCALE_DATA[model.locale]
model.name = locale_data.name
model.dir = locale_data.dir
for p in progress:
if p["data"]["languageId"] == model.id:

View File

@@ -0,0 +1,74 @@
import json
import logging
import random
import string
from datetime import datetime
from uuid import UUID
logger = logging.getLogger("anonymize_backups")
def is_uuid4(value: str):
try:
UUID(value)
return True
except ValueError:
return False
def is_iso_datetime(value: str):
try:
datetime.fromisoformat(value)
return True
except ValueError:
return False
def random_string(length=10):
return "".join(random.choice(string.ascii_lowercase) for _ in range(length))
def clean_value(value):
try:
match value:
# preserve non-strings
case int(value) | float(value):
return value
case None:
return value
# preserve UUIDs and datetimes
case str(value) if is_uuid4(value) or is_iso_datetime(value):
return value
# randomize strings
case str(value):
return random_string()
case _:
pass
except Exception as e:
logger.exception(e)
logger.error(f"Failed to anonymize value: {value}")
return value
def walk_data_and_anonymize(data):
for k, v in data.items():
if isinstance(v, list):
for item in v:
walk_data_and_anonymize(item)
else:
# preserve alembic version number and enums
if k in ["auth_method", "version_num"]:
continue
data[k] = clean_value(v)
def anonymize_database_json(input_filepath: str, output_filepath: str):
with open(input_filepath) as f:
data = json.load(f)
walk_data_and_anonymize(data)
with open(output_filepath, "w") as f:
json.dump(data, f)

View File

@@ -50,7 +50,6 @@ RUN apt-get update \
build-essential \
libpq-dev \
libwebp-dev \
tesseract-ocr-all \
# LDAP Dependencies
libsasl2-dev libldap2-dev libssl-dev \
gnupg gnupg2 gnupg1 \
@@ -89,8 +88,8 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
gosu \
iproute2 \
tesseract-ocr-all \
libldap-common \
libldap-2.5 \
&& rm -rf /var/lib/apt/lists/*
# copying poetry and venv into image
@@ -139,4 +138,4 @@ EXPOSE ${APP_PORT}
COPY ./docker/entry.sh $MEALIE_HOME/run.sh
RUN chmod +x $MEALIE_HOME/run.sh
ENTRYPOINT $MEALIE_HOME/run.sh
ENTRYPOINT ["/app/run.sh"]

View File

@@ -0,0 +1,21 @@
version: "3.4"
services:
mailpit:
image: axllent/mailpit:latest
container_name: mealie_dev_mailpit
restart: no
environment:
- "MP_SMTP_AUTH_ACCEPT_ANY=true"
- "MP_SMTP_AUTH_ALLOW_INSECURE=true"
ports:
- "8025:8025"
- "1025:1025"
postgres:
container_name: mealie_dev_postgres
image: postgres:15
restart: no
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: mealie
POSTGRES_USER: mealie

View File

@@ -1,13 +1,10 @@
# Start Backend API
#!/bin/bash
# Strict Mode
# set -e
# IFS=$'\n\t'
# Start Backend API
# Get PUID/PGID
PUID=${PUID:-911}
PGID=${PGID:-911}
BASH_SOURCE=${BASH_SOURCE:-$0}
add_user() {
groupmod -o -g "$PGID" abc
@@ -15,18 +12,18 @@ add_user() {
}
change_user() {
# If container is started as root then create a new user and switch to it
if [ "$(id -u)" = "0" ]; then
if [ "$(id -u)" = $PUID ]; then
echo "
User uid: $PUID
User gid: $PGID
"
elif [ "$(id -u)" = "0" ]; then
# If container is started as root then create a new user and switch to it
add_user
chown -R $PUID:$PGID /app
echo "Switching to dedicated user"
exec gosu $PUID "$BASH_SOURCE" "$@"
elif [ "$(id -u)" = $PUID ]; then
echo "
User uid: $PUID
User gid: $PGID
"
fi
}
@@ -41,7 +38,7 @@ init() {
poetry run python /app/mealie/db/init_db.py
}
# change_user
change_user
init
GUNICORN_PORT=${API_PORT:-9000}
@@ -49,7 +46,7 @@ GUNICORN_PORT=${API_PORT:-9000}
hostip=`/sbin/ip route|awk '/default/ { print $3 }'`
if [ "$WEB_GUNICORN" = 'true' ]; then
echo "Starting Gunicorn"
gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT --forwarded-allow-ips=$hostip -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload
exec gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT --forwarded-allow-ips=$hostip -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload
else
uvicorn mealie.app:app --host 0.0.0.0 --forwarded-allow-ips=$hostip --port $GUNICORN_PORT
exec uvicorn mealie.app:app --host 0.0.0.0 --forwarded-allow-ips=$hostip --port $GUNICORN_PORT
fi

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -62,7 +62,7 @@ A quality update with major props to [zackbcom](https://github.com/zackbcom) for
### Recipes
- Added user feedback on bad URL
- Better backend data validation for updating recipes, avoid small syntax errors corrupting database entry. [Closes #8](https://github.com/hay-kot/mealie/issues/8)
- Better backend data validation for updating recipes, avoid small syntax errors corrupting database entry. [Closes #8](https://github.com/mealie-recipes/mealie/issues/8)
- Fixed spacing Closes while editing new recipes in JSON
## v0.0.0 - Initial Pre-release

View File

@@ -5,7 +5,7 @@
**Database Version: v0.4.0**
!!! error "Breaking Changes"
1. With a recent refactor some users been experiencing issues with an environmental variable not being set correct. If you are experiencing issues, please provide your comments [Here](https://github.com/hay-kot/mealie/issues/281).
1. With a recent refactor some users been experiencing issues with an environmental variable not being set correct. If you are experiencing issues, please provide your comments [Here](https://github.com/mealie-recipes/mealie/issues/281).
2. If you are a developer, you may experience issues with development as a new environmental variable has been introduced. Setting `PRODUCTION=false` will allow you to develop as normal.
@@ -31,4 +31,4 @@
- Unify Logger across the backend
- mealie.log and last_recipe.json are now downloadable from the frontend from the /admin/about
- New download schema where you request a token and then use that token to hit a single endpoint to download a file. This is a notable change if you are using the API to download backups.
- Recipe images can now be added directly from a URL - [See #117 for details](https://github.com/hay-kot/mealie/issues/117)
- Recipe images can now be added directly from a URL - [See #117 for details](https://github.com/mealie-recipes/mealie/issues/117)

View File

@@ -8,7 +8,7 @@
- Fixed #617 - Section behavior when adding a step
- Fixed #615 - Recipe Settings are not available when creating new recipe
- Fixed #625 - API of today's image returns strange characters
- Fixed [#590](https://github.com/hay-kot/mealie/issues/590) - Duplicate Events when using Gunicorn Workers
- Fixed [#590](https://github.com/mealie-recipes/mealie/issues/590) - Duplicate Events when using Gunicorn Workers
## Features and Improvements

View File

@@ -6,14 +6,14 @@
- FastAPI to 0.78.0
- Recipe Ingredient Editor
- [#1140](https://github.com/hay-kot/mealie/issues/1140) - Error in processing the quantity of ingredients #1140 - UI Now prevents entering not-allowed characters in quantity field
- [#1140](https://github.com/mealie-recipes/mealie/issues/1140) - Error in processing the quantity of ingredients #1140 - UI Now prevents entering not-allowed characters in quantity field
- UI now allows no value to be set in addition to a zero (0) value.
- [#1237](https://github.com/hay-kot/mealie/issues/1237) - UI: Saving a 0 quantity ingredient displays 0 until the page is refreshed #1237 - UI Now properly reacts to changes in the quantity field.
- [#1237](https://github.com/mealie-recipes/mealie/issues/1237) - UI: Saving a 0 quantity ingredient displays 0 until the page is refreshed #1237 - UI Now properly reacts to changes in the quantity field.
- Fix Mealie v0.5.x migration issue [#1183](https://github.com/hay-kot/mealie/issues/1183)
- Fix Mealie v0.5.x migration issue [#1183](https://github.com/mealie-recipes/mealie/issues/1183)
- Consolidated Frontend Types thanks to [@PFischbeck](https://github.com/Fischbeck)
- Added support for SSL/No Auth Email [@nkringle](https://github.com/nkringle)
- [Implement several notifications for server actions ](https://github.com/hay-kot/mealie/pull/1234)[@miroito](https://github.com/Miroito)
- [Implement several notifications for server actions ](https://github.com/mealie-recipes/mealie/pull/1234)[@miroito](https://github.com/Miroito)
- Fix display issue for shared recipe rendering on server [@PFischbeck](https://github.com/Fischbeck)
## v1.0.0b - 2022-05-09
@@ -36,7 +36,7 @@
- Mealie now stores the original text from parsed ingredients, with the ability to peak at the original text from a recipe. [@miroito](https://github.com/Miroito)
- Added some management / utility functions for administrators to manage data and cleanup artifacts from the file system.
- Fix clear url action in recipe creation [#1101](https://github.com/hay-kot/mealie/pull/1101) [@miroito](https://github.com/Miroito)
- Fix clear url action in recipe creation [#1101](https://github.com/mealie-recipes/mealie/pull/1101) [@miroito](https://github.com/Miroito)
- Add group statistics calculations and data storage measurements
- No hard limits are currently imposed on groups - though this may be implemented in the future.

View File

@@ -1,29 +1,29 @@
### Bug Fixes
- Bump isomorphic-dompurify from 0.18.0 to 0.19.0 in /frontend ([#1257](https://github.com/hay-kot/mealie/issues/1257))
- Bump @nuxtjs/auth-next in /frontend ([#1265](https://github.com/hay-kot/mealie/issues/1265))
- Bad dev dependency ([#1281](https://github.com/hay-kot/mealie/issues/1281))
- Add touch support for mealplanner delete ([#1298](https://github.com/hay-kot/mealie/issues/1298))
- Bump isomorphic-dompurify from 0.18.0 to 0.19.0 in /frontend ([#1257](https://github.com/mealie-recipes/mealie/issues/1257))
- Bump @nuxtjs/auth-next in /frontend ([#1265](https://github.com/mealie-recipes/mealie/issues/1265))
- Bad dev dependency ([#1281](https://github.com/mealie-recipes/mealie/issues/1281))
- Add touch support for mealplanner delete ([#1298](https://github.com/mealie-recipes/mealie/issues/1298))
### Documentation
- Add references for VSCode dev containers ([#1299](https://github.com/hay-kot/mealie/issues/1299))
- Docker-compose.dev.yml is currently not functional ([#1300](https://github.com/hay-kot/mealie/issues/1300))
- Add references for VSCode dev containers ([#1299](https://github.com/mealie-recipes/mealie/issues/1299))
- Docker-compose.dev.yml is currently not functional ([#1300](https://github.com/mealie-recipes/mealie/issues/1300))
### Features
- Add reports to bulk recipe import (url) ([#1294](https://github.com/hay-kot/mealie/issues/1294))
- Rewrite print implementation to support new ing ([#1305](https://github.com/hay-kot/mealie/issues/1305))
- Add reports to bulk recipe import (url) ([#1294](https://github.com/mealie-recipes/mealie/issues/1294))
- Rewrite print implementation to support new ing ([#1305](https://github.com/mealie-recipes/mealie/issues/1305))
### Miscellaneous Tasks
- Github stalebot changes ([#1271](https://github.com/hay-kot/mealie/issues/1271))
- Bump eslint-plugin-nuxt in /frontend ([#1258](https://github.com/hay-kot/mealie/issues/1258))
- Bump @vue/runtime-dom in /frontend ([#1259](https://github.com/hay-kot/mealie/issues/1259))
- Bump nuxt-vite from 0.1.3 to 0.3.5 in /frontend ([#1260](https://github.com/hay-kot/mealie/issues/1260))
- Bump vue2-script-setup-transform in /frontend ([#1263](https://github.com/hay-kot/mealie/issues/1263))
- Update dev dependencies ([#1282](https://github.com/hay-kot/mealie/issues/1282))
- Github stalebot changes ([#1271](https://github.com/mealie-recipes/mealie/issues/1271))
- Bump eslint-plugin-nuxt in /frontend ([#1258](https://github.com/mealie-recipes/mealie/issues/1258))
- Bump @vue/runtime-dom in /frontend ([#1259](https://github.com/mealie-recipes/mealie/issues/1259))
- Bump nuxt-vite from 0.1.3 to 0.3.5 in /frontend ([#1260](https://github.com/mealie-recipes/mealie/issues/1260))
- Bump vue2-script-setup-transform in /frontend ([#1263](https://github.com/mealie-recipes/mealie/issues/1263))
- Update dev dependencies ([#1282](https://github.com/mealie-recipes/mealie/issues/1282))
### Refactor
- Split up recipe create page ([#1283](https://github.com/hay-kot/mealie/issues/1283))
- Split up recipe create page ([#1283](https://github.com/mealie-recipes/mealie/issues/1283))

View File

@@ -1,36 +1,36 @@
### Bug Fixes
- Update issue links in v1.0.0beta-2 changelog ([#1312](https://github.com/hay-kot/mealie/issues/1312))
- Bad import path ([#1313](https://github.com/hay-kot/mealie/issues/1313))
- Printer page refs ([#1314](https://github.com/hay-kot/mealie/issues/1314))
- Update issue links in v1.0.0beta-2 changelog ([#1312](https://github.com/mealie-recipes/mealie/issues/1312))
- Bad import path ([#1313](https://github.com/mealie-recipes/mealie/issues/1313))
- Printer page refs ([#1314](https://github.com/mealie-recipes/mealie/issues/1314))
- Consolidate stores to fix mismatched state
- Bump @vue/composition-api from 1.6.1 to 1.6.2 in /frontend ([#1275](https://github.com/hay-kot/mealie/issues/1275))
- Shopping list label editor ([#1333](https://github.com/hay-kot/mealie/issues/1333))
- Bump @vue/composition-api from 1.6.1 to 1.6.2 in /frontend ([#1275](https://github.com/mealie-recipes/mealie/issues/1275))
- Shopping list label editor ([#1333](https://github.com/mealie-recipes/mealie/issues/1333))
### Features
- Default unit fractions to True
- Add unit abbreviation support ([#1332](https://github.com/hay-kot/mealie/issues/1332))
- Attached images by drag and drop for recipe steps ([#1341](https://github.com/hay-kot/mealie/issues/1341))
- Add unit abbreviation support ([#1332](https://github.com/mealie-recipes/mealie/issues/1332))
- Attached images by drag and drop for recipe steps ([#1341](https://github.com/mealie-recipes/mealie/issues/1341))
### Docs
- Render homepage social media link images at 32x32 size ([#1310](https://github.com/hay-kot/mealie/issues/1310))
- Render homepage social media link images at 32x32 size ([#1310](https://github.com/mealie-recipes/mealie/issues/1310))
### Miscellaneous Tasks
- Init git-cliff config
- Bump @types/sortablejs in /frontend ([#1287](https://github.com/hay-kot/mealie/issues/1287))
- Bump @babel/eslint-parser in /frontend ([#1290](https://github.com/hay-kot/mealie/issues/1290))
- Bump @types/sortablejs in /frontend ([#1287](https://github.com/mealie-recipes/mealie/issues/1287))
- Bump @babel/eslint-parser in /frontend ([#1290](https://github.com/mealie-recipes/mealie/issues/1290))
### Refactor
- Unify recipe-organizer components ([#1340](https://github.com/hay-kot/mealie/issues/1340))
- Unify recipe-organizer components ([#1340](https://github.com/mealie-recipes/mealie/issues/1340))
### Security
- Delay server response whenever username is non existing ([#1338](https://github.com/hay-kot/mealie/issues/1338))
- Delay server response whenever username is non existing ([#1338](https://github.com/mealie-recipes/mealie/issues/1338))
### Wip
- Pagination-repository ([#1316](https://github.com/hay-kot/mealie/issues/1316))
- Pagination-repository ([#1316](https://github.com/mealie-recipes/mealie/issues/1316))

View File

@@ -63,57 +63,57 @@ If either of the above actions prevent the user from uploading images, the appli
### Bug Fixes
- For erroneously-translated datetime config ([#1362](https://github.com/hay-kot/mealie/issues/1362))
- Fixed text color on RecipeCard in RecipePrintView and implemented ingredient sections ([#1351](https://github.com/hay-kot/mealie/issues/1351))
- Ingredient sections lost after parsing ([#1368](https://github.com/hay-kot/mealie/issues/1368))
- Increased float rounding precision for CRF parser ([#1369](https://github.com/hay-kot/mealie/issues/1369))
- Infinite scroll bug on all recipes page ([#1393](https://github.com/hay-kot/mealie/issues/1393))
- Fast fail of bulk importer ([#1394](https://github.com/hay-kot/mealie/issues/1394))
- Bump @mdi/js from 5.9.55 to 6.7.96 in /frontend ([#1279](https://github.com/hay-kot/mealie/issues/1279))
- Bump @nuxtjs/i18n from 7.0.3 to 7.2.2 in /frontend ([#1288](https://github.com/hay-kot/mealie/issues/1288))
- Bump date-fns from 2.23.0 to 2.28.0 in /frontend ([#1293](https://github.com/hay-kot/mealie/issues/1293))
- Bump fuse.js from 6.5.3 to 6.6.2 in /frontend ([#1325](https://github.com/hay-kot/mealie/issues/1325))
- Bump core-js from 3.17.2 to 3.23.1 in /frontend ([#1383](https://github.com/hay-kot/mealie/issues/1383))
- All-recipes page now sorts alphabetically ([#1405](https://github.com/hay-kot/mealie/issues/1405))
- Sort recent recipes by created_at instead of date_added ([#1417](https://github.com/hay-kot/mealie/issues/1417))
- Only show scaler when ingredients amounts enabled ([#1426](https://github.com/hay-kot/mealie/issues/1426))
- Add missing types for API token deletion ([#1428](https://github.com/hay-kot/mealie/issues/1428))
- Entry nutrition checker ([#1448](https://github.com/hay-kot/mealie/issues/1448))
- Use == operator instead of is_ for sql queries ([#1453](https://github.com/hay-kot/mealie/issues/1453))
- Use `mtime` instead of `ctime` for backup dates ([#1461](https://github.com/hay-kot/mealie/issues/1461))
- Mealplan pagination ([#1464](https://github.com/hay-kot/mealie/issues/1464))
- Properly use pagination for group event notifies ([#1512](https://github.com/hay-kot/mealie/pull/1512))
- For erroneously-translated datetime config ([#1362](https://github.com/mealie-recipes/mealie/issues/1362))
- Fixed text color on RecipeCard in RecipePrintView and implemented ingredient sections ([#1351](https://github.com/mealie-recipes/mealie/issues/1351))
- Ingredient sections lost after parsing ([#1368](https://github.com/mealie-recipes/mealie/issues/1368))
- Increased float rounding precision for CRF parser ([#1369](https://github.com/mealie-recipes/mealie/issues/1369))
- Infinite scroll bug on all recipes page ([#1393](https://github.com/mealie-recipes/mealie/issues/1393))
- Fast fail of bulk importer ([#1394](https://github.com/mealie-recipes/mealie/issues/1394))
- Bump @mdi/js from 5.9.55 to 6.7.96 in /frontend ([#1279](https://github.com/mealie-recipes/mealie/issues/1279))
- Bump @nuxtjs/i18n from 7.0.3 to 7.2.2 in /frontend ([#1288](https://github.com/mealie-recipes/mealie/issues/1288))
- Bump date-fns from 2.23.0 to 2.28.0 in /frontend ([#1293](https://github.com/mealie-recipes/mealie/issues/1293))
- Bump fuse.js from 6.5.3 to 6.6.2 in /frontend ([#1325](https://github.com/mealie-recipes/mealie/issues/1325))
- Bump core-js from 3.17.2 to 3.23.1 in /frontend ([#1383](https://github.com/mealie-recipes/mealie/issues/1383))
- All-recipes page now sorts alphabetically ([#1405](https://github.com/mealie-recipes/mealie/issues/1405))
- Sort recent recipes by created_at instead of date_added ([#1417](https://github.com/mealie-recipes/mealie/issues/1417))
- Only show scaler when ingredients amounts enabled ([#1426](https://github.com/mealie-recipes/mealie/issues/1426))
- Add missing types for API token deletion ([#1428](https://github.com/mealie-recipes/mealie/issues/1428))
- Entry nutrition checker ([#1448](https://github.com/mealie-recipes/mealie/issues/1448))
- Use == operator instead of is_ for sql queries ([#1453](https://github.com/mealie-recipes/mealie/issues/1453))
- Use `mtime` instead of `ctime` for backup dates ([#1461](https://github.com/mealie-recipes/mealie/issues/1461))
- Mealplan pagination ([#1464](https://github.com/mealie-recipes/mealie/issues/1464))
- Properly use pagination for group event notifies ([#1512](https://github.com/mealie-recipes/mealie/pull/1512))
### Documentation
- Add go bulk import example ([#1388](https://github.com/hay-kot/mealie/issues/1388))
- Add go bulk import example ([#1388](https://github.com/mealie-recipes/mealie/issues/1388))
- Fix old link
- Pagination and filtering, and fixed a few broken links ([#1488](https://github.com/hay-kot/mealie/issues/1488))
- Pagination and filtering, and fixed a few broken links ([#1488](https://github.com/mealie-recipes/mealie/issues/1488))
### Features
- Toggle display of ingredient references in recipe instructions ([#1268](https://github.com/hay-kot/mealie/issues/1268))
- Add custom scaling option ([#1345](https://github.com/hay-kot/mealie/issues/1345))
- Implemented "order by" API parameters for recipe, food, and unit queries ([#1356](https://github.com/hay-kot/mealie/issues/1356))
- Implement user favorites page ([#1376](https://github.com/hay-kot/mealie/issues/1376))
- Extend Apprise JSON notification functionality with programmatic data ([#1355](https://github.com/hay-kot/mealie/issues/1355))
- Mealplan-webhooks ([#1403](https://github.com/hay-kot/mealie/issues/1403))
- Added "last-modified" header to supported record types ([#1379](https://github.com/hay-kot/mealie/issues/1379))
- Re-write get all routes to use pagination ([#1424](https://github.com/hay-kot/mealie/issues/1424))
- Advanced filtering API ([#1468](https://github.com/hay-kot/mealie/issues/1468))
- Restore frontend sorting for all recipes ([#1497](https://github.com/hay-kot/mealie/issues/1497))
- Implemented local storage for sorting and dynamic sort icons on the new recipe sort card ([1506](https://github.com/hay-kot/mealie/pull/1506))
- create new foods and units from their Data Management pages ([#1511](https://github.com/hay-kot/mealie/pull/1511))
- Toggle display of ingredient references in recipe instructions ([#1268](https://github.com/mealie-recipes/mealie/issues/1268))
- Add custom scaling option ([#1345](https://github.com/mealie-recipes/mealie/issues/1345))
- Implemented "order by" API parameters for recipe, food, and unit queries ([#1356](https://github.com/mealie-recipes/mealie/issues/1356))
- Implement user favorites page ([#1376](https://github.com/mealie-recipes/mealie/issues/1376))
- Extend Apprise JSON notification functionality with programmatic data ([#1355](https://github.com/mealie-recipes/mealie/issues/1355))
- Mealplan-webhooks ([#1403](https://github.com/mealie-recipes/mealie/issues/1403))
- Added "last-modified" header to supported record types ([#1379](https://github.com/mealie-recipes/mealie/issues/1379))
- Re-write get all routes to use pagination ([#1424](https://github.com/mealie-recipes/mealie/issues/1424))
- Advanced filtering API ([#1468](https://github.com/mealie-recipes/mealie/issues/1468))
- Restore frontend sorting for all recipes ([#1497](https://github.com/mealie-recipes/mealie/issues/1497))
- Implemented local storage for sorting and dynamic sort icons on the new recipe sort card ([1506](https://github.com/mealie-recipes/mealie/pull/1506))
- create new foods and units from their Data Management pages ([#1511](https://github.com/mealie-recipes/mealie/pull/1511))
### Miscellaneous Tasks
- Bump dev deps ([#1418](https://github.com/hay-kot/mealie/issues/1418))
- Bump @vue/runtime-dom in /frontend ([#1423](https://github.com/hay-kot/mealie/issues/1423))
- Backend page_all route cleanup ([#1483](https://github.com/hay-kot/mealie/issues/1483))
- Bump dev deps ([#1418](https://github.com/mealie-recipes/mealie/issues/1418))
- Bump @vue/runtime-dom in /frontend ([#1423](https://github.com/mealie-recipes/mealie/issues/1423))
- Backend page_all route cleanup ([#1483](https://github.com/mealie-recipes/mealie/issues/1483))
### Refactor
- Remove depreciated repo call ([#1370](https://github.com/hay-kot/mealie/issues/1370))
- Remove depreciated repo call ([#1370](https://github.com/mealie-recipes/mealie/issues/1370))
### Hotfix
@@ -121,6 +121,6 @@ If either of the above actions prevent the user from uploading images, the appli
### UI
- Improve parser ui text display ([#1437](https://github.com/hay-kot/mealie/issues/1437))
- Improve parser ui text display ([#1437](https://github.com/mealie-recipes/mealie/issues/1437))
<!-- generated by git-cliff -->

View File

@@ -13,15 +13,15 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu
3. If you're interested on working on major changes please get in touch on discord and coordinate with other developers. No sense in doubling up on work if someones already on it.
4. Once you've got an idea of what changes you want to make, create a draft PR as soon as you can to let us know what you're working on and how we can help!
5. If you've changed APIs, update the documentation.
6. Run tests, including `make backend-all`. Note that the tests do not clean up after themselves and leave things in the database. So be sure to also run `make clean-data` and/or `make backend-clean` inbetween major testing rounds to be sure that you aren't testing on old data.
6. Run tests, including `task py:check`.
6. Issue that pull request! First make a draft PR, make sure that the automated github tests all pass, then mark as ready for review.
7. Be sure to add release notes to the pull request.
## Any contributions you make will be under the AGPL Software License
In short, when you submit code changes, your submissions are understood to be under the same [AGPL License](https://choosealicense.com/licenses/agpl-3.0/) that covers the project. Feel free to contact the maintainers if that's a concern.
## Report bugs using Github's [issues](https://github.com/hay-kot/mealie/issues)
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/hay-kot/mealie/issues/new); it's that easy!
## Report bugs using Github's [issues](https://github.com/mealie-recipes/mealie/issues)
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/mealie-recipes/mealie/issues/new); it's that easy!
## Write bug reports with detail, background, and sample code
**Great Bug Reports** tend to have:

View File

@@ -1,6 +1,6 @@
# Maintainers Guide
This is the start of the maintainers guide for Mealie developers. Those who have been invited to the GitHub organization and/or those who whish to play a bigger part in the Mealie developers community may find this helpful.
This is the start of the maintainers guide for Mealie developers. Those who have been invited to the GitHub organization and/or those who wish to play a bigger part in the Mealie developers community may find this helpful.
## Managing Issues
@@ -13,7 +13,7 @@ After you've reviered an issue it will generally move into one of two states:
`needs more info`
: The orignal post does not contain enough information, and if the reporter does not provide additional information, the issue will be automatically closed.
: The original post does not contain enough information, and if the reporter does not provide additional information, the issue will be automatically closed.
Once you've reviewed an issue and moved it into another category, you should remove the triage label.
@@ -40,18 +40,38 @@ Mealie is published via GitHub actions to the GitHub container registry with the
: published when a new GitHub Release is created - [Actions File](https://github.com/mealie-recipes/mealie/blob/mealie-next/.github/workflows/release.yml)
!!! note
Both the latest, and {version} tags will be the same container on the release of a new version
Both the latest, and {version} tags will be the same image on the release of a new version
### Process
Because we've built all our publishing effors on GitHub Actions we rely primarily on automations to perform our releases. As such creating a new build of Mealie is as simple as creating a new GitHub release. Here are the general steps we take to create a new release
Because we've built all our publishing efforts on GitHub Actions we rely primarily on automations to perform our releases. As such creating a new build of Mealie is as simple as creating a new GitHub release. Here are the general steps we take to create a new release
1. Navigate to the [Github Release Page](https://github.com/mealie-recipes/mealie/releases) and click the 'Draft a new release' button.
2. Choose a tag and increment the version according to the semver specification. i.e, **major** version for breaking changes, **minor** for feature updates, and **patch** for bug fixes.
3. Name the Release, usually just the tag is fine, however if there is a special feature you'd like to higlight this would be a great place to do it.
3. Name the Release, usually just the tag is fine, however if there is a special feature you'd like to highlight this would be a great place to do it.
4. Click the "Generate release notes" button which will pull in all the Git Commits as a changelog. For bug fix only releases this is sufficient, however if there are major features, or good quality of life improvements it's good to provide those prior to listing the full changelog.
!!! tip
Don't worry about setting the version number in the container or code, it's set during the build process and uses the tag you specified when drafting a new release.
You can see how this is done in the [Actions File](https://github.com/mealie-recipes/mealie/blob/mealie-next/.github/workflows/partial-builder.yml#L35-L37)
### Tags and Releases
Mealie tries to adhere to a strict [Semver](https://semver.org/) policy. This means that we try to keep our releases as stable as possible, and only introduce breaking changes when absolutely necessary. As such we try to keep our releases as follows:
- **Major** releases are reserved for breaking changes, and are not expected to be frequent. Ideally, we will remain at v1.x.x for the forseeable future.
- **Minor** releases are reserved for new features, and are expected to be frequent.
- **Patch** releases are reserved for bug fixes, and are expected to be frequent.
Any maintainer who has privileges on GitHub to create a new release can create a release at any time they feel it is necessary. However, it is recommended that you reach out in the discord to other maintainers and get at least one other maintainer to approve the release.
An important caveat to this is that we _may_ make breaking changes in a minor release if it is security related. In this case, the releaser should headline the release notes with the notice and impact of the breaking change, however we may not bump the major version depending on user impact.
### Release Notes
When drafting a new release, GitHub will automatically pull in all the commits since the last release. This is a great start. After pulling in all of the commits, you should add sections for
- New Features - Any new features that are being introduced in this release (screenshots are great here)
- Bug Fixes - Significant bug fixes that are being introduced in this release, smaller bug fixes can be left out if they are noted in a commit message
- Breaking Changes - Any breaking changes that are being introduced in this release (should be rare)

View File

@@ -14,12 +14,12 @@ Prerequisites
- Visual Studio Code
### Linux and MacOS
First ensure that docker is running. Then when you clone the repo and open with VS Code you should see a popup asking you to reopen the project inside a development container. Click yes and it will build the development container and run the setup required to run both the backend API and the frontend webserver. This also pre-configures pre-commit hooks to ensure that the code is up to date before committing.
### Windows
Make sure the VSCode Dev Containers extension is installed, then select "Dev Containers: Clone Repository in Container Volume..." in the command pallete (F1). Select your forked repo and choose the `mealie-next` branch, which contains the latest changes. This mounts your repository directly in WSL2, which [greatly improves the performance of the container](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume), and enables hot-reloading for the frontend. Running the container on a mounted volume may not work correctly on Windows due to WSL permission mapping issues.
[Checkout the makefile reference](#make-file-reference) for all of the available commands.
Make sure the VSCode Dev Containers extension is installed, then select "Dev Containers: Clone Repository in Container Volume..." in the command palette (F1). Select your forked repo and choose the `mealie-next` branch, which contains the latest changes. This mounts your repository directly in WSL2, which [greatly improves the performance of the container](https://code.visualstudio.com/docs/devcontainers/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume), and enables hot-reloading for the frontend. Running the container on a mounted volume may not work correctly on Windows due to WSL permission mapping issues.
!!! tip
For slow terminal checkout the solution in this [GitHub Issue](https://github.com/microsoft/vscode/issues/133215)
@@ -29,16 +29,18 @@ Make sure the VSCode Dev Containers extension is installed, then select "Dev Con
```
## Without Dev Containers
### Prerequisites
- [Python 3.10](https://www.python.org/downloads/)
- [Poetry](https://python-poetry.org/docs/#installation)
- [Node v16.x](https://nodejs.org/en/)
- [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable)
- [task](https://taskfile.dev/#/installation)
### Installing Dependencies
Once the prerequisites are installed you can cd into the project base directory and run `make setup` to install the python and node dependencies.
Once the prerequisites are installed you can cd into the project base directory and run `task setup` to install the python and node dependencies, and download the NLP model.
=== "Linux / macOS"
@@ -46,29 +48,16 @@ Once the prerequisites are installed you can cd into the project base directory
# Naviate To The Root Directory
cd /path/to/project
# Utilize the Makefile to Install Dependencies
make setup
# Utilize the Taskfile to Install Dependencies
task setup
```
=== "Windows"
``` powershell
# Install Python Dependencies
Set-Directory -Path "C:\path\to\project"
poetry install
# Install Node Dependencies
Set-Directory frontend
yarn install
```
### Setting ENV Variables
Before you start the server you MUST copy the `template.env` and `frontend/template.env` files to their respective locations with the name `.env` and `frontend/.env` respectively. The application will-not run without these files.
## Postgres
- Whether using a container or manual install, you need to set up your own postgres dev server. The database, username, password, etc should match the `POSTGRES_*` options located in the `.env` file.
- Install psycog2 with `poetry install -E pgsql` (in the main `mealie` directory, *not* `frontend`)
The taskfile has two commands that need to be run to run the development environment against a postgres database.
- `task dev:services` - This will start the postgres database, and a smtp server for email testing.
- `task py:postgres` - This will run that backend API configured for the local postgres database.
## Starting The Server
@@ -78,57 +67,24 @@ Now you're ready to start the servers. You'll need two shells open, One for the
```bash
# Terminal #1
make backend
task py
# Terminal #2
make frontend
task ui
```
=== "Windows"
``` powershell
# Terminal # 1
poetry run python mealie/db/init_db.py # Initialize the database
poetry run python mealie/app.py # start application
# Terminal # 2
Set-Directory frontend
yarn run dev
```
## Make File Reference
Run `make help` for reference. If you're on a system that doesn't support makefiles in most cases you can use the commands directly in your terminal by copy/pasting them from the Makefile.
```
docs 📄 Start Mkdocs Development Server
code-gen 🤖 Run Code-Gen Scripts
setup 🏗 Setup Development Instance
setup-model 🤖 Get the latest NLP CRF++ Model
clean-data ⚠️ Removes All Developer Data for a fresh server start
clean-pyc 🧹 Remove Python file artifacts
clean-test 🧹 Remove test and coverage artifacts
backend-clean 🧹 Remove all build, test, coverage and Python artifacts
backend-test 🧪 Run tests quickly with the default Python
backend-format 🧺 Format, Check and Flake8
backend-all 🧪 Runs all the backend checks and tests
backend-coverage ☂️ Check code coverage quickly with the default Python
backend 🎬 Start Mealie Backend Development Server
frontend 🎬 Start Mealie Frontend Development Server
frontend-build 🏗 Build Frontend in frontend/dist
frontend-generate 🏗 Generate Code for Frontend
frontend-lint 🧺 Run yarn lint
docker-dev 🐳 Build and Start Docker Development Stack (currently not functional, see #756, #1072)
docker-prod 🐳 Build and Start Docker Production Stack
```
## Internationalization
### Frontend
We use vue-i18n package for internationalization. Translations are stored in json format located in [frontend/lang/messages](https://github.com/hay-kot/mealie/tree/mealie-next/frontend/lang/messages).
We use vue-i18n package for internationalization. Translations are stored in json format located in [frontend/lang/messages](https://github.com/mealie-recipes/mealie/tree/mealie-next/frontend/lang/messages).
### Backend
Translations are stored in json format located in [mealie/lang/messages](https://github.com/hay-kot/mealie/tree/mealie-next/mealie/lang/messages).
Translations are stored in json format located in [mealie/lang/messages](https://github.com/mealie-recipes/mealie/tree/mealie-next/mealie/lang/messages).
### Quick frontend localization with VS Code
[i18n Ally for VScode](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) is helpful for generating new strings to translate using Code Actions. It also has a nice feature, which shows translations in-place when editing code.
A few settings must be tweaked to make the most of its features. Some settings are stored on project level, but most of them have to be set manually in your workspace or user settings.\

View File

@@ -15,6 +15,6 @@ Alternatively, you can register a new parser by fulfilling the `ABCIngredientPar
## Links
- [Pretrained Model](https://github.com/hay-kot/mealie-nlp-model)
- [Pretrained Model](https://github.com/mealie-recipes/mealie-nlp-model)
- [CRF++ (Forked)](https://github.com/hay-kot/crfpp)

View File

@@ -1,5 +1,5 @@
!!! info
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
In a lot of ways, Home Assistant is why this project exists! Since Mealie has a robust API it makes it a great fit for interacting with Home Assistant and pulling information into your dashboard.

View File

@@ -3,37 +3,80 @@
!!! info
This guide was submitted by a community member. Find something wrong? Submit a PR to get it fixed!
![Image from apple site](https://help.apple.com/assets/5E8CEA35094622DF10489984/5E8CEA42094622DF1048998D/en_US/ed1f9c157cdefc13e0161e0f70015455.png)
User [brasilikum](https://github.com/brasilikum) opened an issue on the main repo about how they had created an [iOS shortcut](https://github.com/hay-kot/mealie/issues/103) for interested users.
This original method broke after the transition to version 1.X and an issue was raised on [Github](https://github.com/hay-kot/mealie/issues/2092) GitHub user [Zippyy](https://github.com/zippyy) has helped to create a working shortcut for version 1.X.
This is a useful utility for iOS users who browse for recipes in their web browser from their devices.
Don't know what an iOS shortcut is? Neither did I! Experienced iOS users may already be familiar with this utility but for the uninitiated, here is the official Apple explanation:
> A shortcut is a quick way to get one or more tasks done with your apps. The Shortcuts app lets you create your own shortcuts with multiple steps. For example, build a “Surf Time” shortcut that grabs the surf report, gives an ETA to the beach, and launches your surf music playlist.
Basically it is a visual scripting language that lets a user build an automation in a guided fashion. The automation can be [shared with anyone](https://www.icloud.com/shortcuts/cc568d1615bc4f998789f85d1ef74846) but if it is a user creation, you'll have to jump through a few hoops to make an untrusted automation work on your device.
Basically it is a visual scripting language that lets a user build an automation in a guided fashion. The automation can be [shared with anyone](https://www.icloud.com/shortcuts/94aa272af5ff4d2c8fe5e13a946f89a9) but if it is a user creation, you'll have to jump through a few hoops to make an untrusted automation work on your device.
This guide assumes that you already know how to [generate API tokens](https://hay-kot.github.io/mealie/documentation/users-groups/user-settings/#api-key-generation) for your user that intends to use an iOS shortcut.
## Setup Video
First, click the [link](https://www.icloud.com/shortcuts/cc568d1615bc4f998789f85d1ef74846) and begin the setup of the shortcut.
The following YouTube video walks through setting up the shortcut in 3 minutes for those who prefer following along visually.
<iframe width="560" height="315" src="https://www.youtube.com/embed/XZk6S1MVUrE?si=HGH07RbK-Ip_1qFz" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
![screenshot](../../assets/img/sc1half.png)
## Guide
### Prerequisites
Before setting up the shortcut, make sure you have the following information ready and easily accessable on your Apple device.
1. The URL of your Mealie instance
2. An API Key for your user
3. A Gemini API Key from [Google AI Studio](https://makersuite.google.com)
!!! note
A Gemini API Key is not required for importing URLs from Safari or your Camera, however you will not be able to take a photo of a recipe and import it without a Gemini key.
Google AI Studio is currently only available in [certain countries and languages](https://ai.google.dev/available_regions). Most notably it is not currently available in Europe.
### Setup
On the Apple device you wish to add the shortcut to, click on [this link](https://www.icloud.com/shortcuts/94aa272af5ff4d2c8fe5e13a946f89a9) to begin the setup of the shortcut.
![screenshot](../../assets/img/ios/setup.png)
Next, you need to replace `url` and `port` with the information for your Mealie instance.
If you have a domain that you use (e.g. `https://mealie.example.com`), put that here. If you just run local, then you need to put in your Mealie instance IP and the port you use (e.g. the default is `9925`).
![screenshot](../../assets/img/ios/url.png)
![screenshot](../../assets/img/sc2half.png)
Next, you need to replace `MEALIE_API_KEY` with your API token.
![screenshot](../../assets/img/ios/api.png)
Finally, you need to replace the word `keyhere` with your API token. Keep the word `Bearer`!!!
Finally, replace `GEMINI_API_KEY` with the one you got from [Google AI Studio](https://makersuite.google.com)
![screenshot](../../assets/img/ios/gemini.png)
![screenshot](../../assets/img/sc3half.png)
You may wish to [add the shortcut to your home screen](https://support.apple.com/guide/shortcuts/add-a-shortcut-to-the-home-screen-apd735880972/ios) for easier access.
You should now be able to share a website to the shortcut and have Mealie grab all the necessary information!
## Features
- Share a website from Safari with Mealie to import via URL.
- Share a recipe photo from photos to perform OCR and import a physical recipe.
- Trigger the shortcut and take a photo of a physical recipe to import.
- Trigger the shortcut to select a photo from your Photos app to import.
- Trigger the shortcut to take a picture of a URL (like on the bottom of a printed recipe) to import.
## Troubleshooting
Sometimes Gemini will not be able to parse a recipe, and you will get an error. Users have found success with a combination of the following:
1. #### Try Again
Sometimes Gemini returns the wrong information which causes the import to fail. Often, trying again will be successful.
2. #### Photo Quality
Make sure there is no large glare or shadow over the picture, and you have all the text in frame.
3. #### Edit the Photo
Users have found success by cropping the picture to just the recipe card, adding a "mono" filter, and cranking up the exposure before importing.
## History
User [brasilikum](https://github.com/brasilikum) opened an issue on the main repo about how they had created an [iOS shortcut](https://github.com/mealie-recipes/mealie/issues/103) for interested users.
This original method broke after the transition to version 1.X and an issue was raised on [Github](https://github.com/mealie-recipes/mealie/issues/2092) GitHub user [Zippyy](https://github.com/zippyy) has helped to create a working shortcut for version 1.X.
When OCR was removed from Mealie, GitHub user [hunterjm](https://github.com/zippyy) created a new shortcut that uses Apple's built-in OCR and Google Gemini to enhance and replace that functionality.

View File

@@ -5,7 +5,7 @@
To make the setup of a Reverse Proxy much easier, Linuxserver.io developed [SWAG](https://github.com/linuxserver/docker-swag)
To make the setup of a Reverse Proxy much easier, Linuxserver.io developed [SWAG](https://github.com/linuxserver/docker-swag)
SWAG - Secure Web Application Gateway (formerly known as letsencrypt, no relation to Let's Encrypt™) sets up an Nginx web server and reverse proxy with PHP support and a built-in certbot client that automates free SSL server certificate generation and renewal processes (Let's Encrypt and ZeroSSL). It also contains fail2ban for intrusion prevention.
## Step 1: Get a domain
@@ -14,39 +14,38 @@ The first step is to grab a dynamic DNS if you don't have your own subdomain alr
## Step 2: Set-up SWAG
Then you will need to set up SWAG, the variables of the docker-compose are explained on the Github page of [SWAG](https://github.com/linuxserver/docker-swag).
This is an example of how to set it up using duckdns and docker-compose.
Then you will need to set up SWAG, the variables of the docker-compose.yaml file are explained on the Github page of [SWAG](https://github.com/linuxserver/docker-swag).
This is an example of how to set it up using duckdns and docker compose.
!!! example "docker-compose.yml"
!!! example "docker-compose.yaml"
```yaml
version: "3.1"
services:
swag:
image: ghcr.io/linuxserver/swag
container_name: swag
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Brussels
- URL=<mydomain.duckdns>
- SUBDOMAINS=wildcard
- VALIDATION=duckdns
- CERTPROVIDER= #optional
- DNSPLUGIN= #optional
- DUCKDNSTOKEN=<duckdnstoken>
- EMAIL=<e-mail> #optional
- ONLY_SUBDOMAINS=false #optional
- EXTRA_DOMAINS=<extradomains> #optional
- STAGING=false #optional
volumes:
- /etc/config/swag:/config
ports:
- 443:443
- 80:80 #optional
restart: unless-stopped
swag:
image: ghcr.io/linuxserver/swag
container_name: swag
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Brussels
- URL=<mydomain.duckdns>
- SUBDOMAINS=wildcard
- VALIDATION=duckdns
- CERTPROVIDER= #optional
- DNSPLUGIN= #optional
- DUCKDNSTOKEN=<duckdnstoken>
- EMAIL=<e-mail> #optional
- ONLY_SUBDOMAINS=false #optional
- EXTRA_DOMAINS=<extradomains> #optional
- STAGING=false #optional
volumes:
- /etc/config/swag:/config
ports:
- 443:443
- 80:80 #optional
restart: unless-stopped
```
Don't forget to change the <code>mydomain.duckns</code> into your personal domain and the <code>duckdnstoken</code> into your token and remove the brackets.
@@ -61,26 +60,25 @@ Alternatively, you can create a new file <code>mealie.subdomain.conf</code> in p
!!! example "mealie.subdomain.conf"
```yaml
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mealie.*;
server_name mealie.*;
include /config/nginx/ssl.conf;
include /config/nginx/ssl.conf;
client_max_body_size 0;
client_max_body_size 0;
location / {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app mealie-frontend;
set $upstream_port 3000;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
location / {
include /config/nginx/proxy.conf;
include /config/nginx/resolver.conf;
set $upstream_app mealie-frontend;
set $upstream_port 3000;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
}
}
```
## Step 4: Port-forward port 443

View File

@@ -2,7 +2,7 @@
## How do I enable "smart" ingredient handling?
You might have noticed that scaling up a recipe or making a shopping list doesn't by default handle the ingredients in a way you might expect. Depending on your settings, scaling up might yield things like `2 1 cup broth` instead of `2 cup broth`. And making shopping lists from reciepes that have shared ingredients can yield multiple lines of the same ingredient. **But** mealie has a mechanism to intelligently handle ingredients and make your day better. How?
You might have noticed that scaling up a recipe or making a shopping list doesn't by default handle the ingredients in a way you might expect. Depending on your settings, scaling up might yield things like `2 1 cup broth` instead of `2 cup broth`. And making shopping lists from reciepes that have shared ingredients can yield multiple lines of the same ingredient. **But**, mealie has a mechanism to intelligently handle ingredients and make your day better. How?
### Set up your Foods and Units
Do the following just **once**. Doing this applies to your whole group, so be careful.
@@ -26,9 +26,9 @@ Do the following for each recipe you want to intelligently handle ingredients.
6. Click the Edit button/icon again
7. Scroll to the ingredients and you should see new fields for Amount, Unit, Food, and Note. The Note in particular will contain the original text of the Recipe.
8. Click `Parse` and you will be taken to the ingredient parsing page.
9. Choose your parser. the `Natural Language Parser` works very well, but you can also use the `Brute Parser`.
10. Click `Parse All` and your ingredients should be separated out into Units and Foods based on your seeding in Step 1 above.
11. For ingredients where the Unit or Food were not found, you can click a button to accept an automatically suggested Food to add to the database. Or manually enter the Unit/Food and hit `Enter` (or click `Create`) to add it to the database
9. Choose your parser. The `Natural Language Parser` works very well, but you can also use the `Brute Parser`.
10. Click `Parse All`, and your ingredients should be separated out into Units and Foods based on your seeding in Step 1 above.
11. For ingredients where the Unit or Food was not found, you can click a button to accept an automatically suggested Food to add to the database. Or, manually enter the Unit/Food and hit `Enter` (or click `Create`) to add it to the database
12. When done, click `Save All` and you will be taken back to the recipe. Now the Unit and Food fields of the recipe should be filled out.
Scaling up this recipe or adding it to a Shopping List will now smartly take care of ingredient amounts and duplicate combinations.
@@ -41,22 +41,15 @@ Yes. If you are using the v1 branches (including beta), you can upgrade to the l
## How can I change the theme?
You can change the theme by settings the environment variables on the frontend container.
You can change the theme by settings the environment variables.
- [Frontend Theme](../installation/frontend-config#themeing)
## How can I change the language?
Languages need to be set on the frontend and backend containers as ENV variables.
- [Frontend Config](../installation/frontend-config/)
- [Backend Config](../installation/backend-config/)
- [Backend Config - Themeing](./installation/backend-config.md#themeing)
## How can I change the Login Session Timeout?
Login session can be configured by setting the `TOKEN_TIME` variable on the backend container.
- [Backend Config](../installation/backend-config/)
- [Backend Config](./installation/backend-config.md)
## Can I serve Mealie on a subpath?
@@ -64,9 +57,7 @@ No. Due to limitations from the Javascript Framework, mealie doesn't support ser
## Can I install Mealie without docker?
Yes, you can install Mealie on your local machine. HOWEVER, it is recommended that you don't. Managing non-system versions of python, node, and npm is a pain. Moreover updating and upgrading your system with this configuration is unsupported and will likely require manual interventions. If you insist on installing Mealie on your local machine, you can use the links below to help guide your path.
- [Advanced Installation](../installation/advanced/)
Yes, you can install Mealie on your local machine. HOWEVER, it is recommended that you don't. Managing non-system versions of python, node, and npm is a pain. Moreover, updating and upgrading your system with this configuration is unsupported and will likely require manual interventions.
## What is fuzzy search and how do I use it?
Mealie can use fuzzy search, which is robust to minor typos. For example, searching for "brocolli" will still find your recipe for "broccoli soup". But fuzzy search is only functional on a Postgres database backend. To enable fuzzy search you will need to migrate to Postgres:
@@ -75,9 +66,9 @@ Mealie can use fuzzy search, which is robust to minor typos. For example, search
2. Set up a [Postgres](./installation/postgres.md) instance of Mealie
3. Upload the backup .zip and click to apply it (as as migration)
## How i can attach an image or video to a Recipe?
## How can I attach an image or video to a Recipe?
Yes. Mealie's Recipe Steps and other fields support the markdown syntax and therefor supports images and videos. To attach an image to the recipe, you can upload it as an asset and use the provided copy button to generate the html image tag required to render the image. For videos, Mealie provides no way to host videos. You'll need to host your videos with another provider and embed them in your recipe. Generally, the video provider will provide a link to the video and the html tag required to render the video. For example, youtube provides the following link that works inside a step. You can adjust the width and height attributes as necessary to ensure a fit.
Mealie's Recipe Steps and other fields support markdown syntax and therefore support images and videos. To attach an image to the recipe, you can upload it as an asset and use the provided copy button to generate the html image tag required to render the image. For videos, Mealie provides no way to host videos. You'll need to host your videos with another provider and embed them in your recipe. Generally, the video provider will provide a link to the video and the html tag required to render the video. For example, YouTube provides the following link that works inside a step. You can adjust the width and height attributes as necessary to ensure a fit.
```html
<iframe width="560" height="315" src="https://www.youtube.com/embed/nAUwKeO93bY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@@ -85,7 +76,7 @@ Yes. Mealie's Recipe Steps and other fields support the markdown syntax and ther
## How can I unlock my account?
If your account has been locked by bad password attempts, you can use an administrator account to unlock another account. Alternatively you can unlock all account via a scripts within the container.
If your account has been locked by bad password attempts, you can use an administrator account to unlock another account. Alternatively, you can unlock all accounts via a script within the container.
```shell
docker exec -it mealie-next bash
@@ -93,7 +84,7 @@ docker exec -it mealie-next bash
python /app/mealie/scripts/reset_locked_users.py
```
## How can I change my password
## How can I change my password?
You can change your password by going to the user profile page and clicking the "Change Password" button. Alternatively you can use the following script to change your password via the CLI if you are locked out of your account.
@@ -107,8 +98,9 @@ python /app/mealie/scripts/change_password.py
Managing private groups and recipes can be confusing. The following diagram and notes should help explain how they work to determine if a recipe can be shared publicly.
- Private links that are generated using th`Share` button bypass all group and recipe permissions.
- Private groups block all access to recipes, including those that are public. Expect as noted above.
- Private links that are generated from the recipe page using the `Share` button bypass all group and recipe permissions
- Private groups block all access to recipes, including those that are public, except as noted above.
- Groups with "Allow users outside of your group to see your recipes" disabled block all access to recipes, except as noted above.
- Private recipes block all access to the recipe from public links. This does not affect Private Links.
```mermaid
@@ -132,8 +124,10 @@ stateDiagram-v2
p3 --> n1: No
```
For more information, check out the [Permissions and Public Access guide](./usage/permissions-and-public-access.md).
## Can I use fail2ban with mealie?
Yes, mealie is configured to properly forward external IP addresses into the `mealie.log` logfile. Note that, due to restrictions in docker, IP address forwarding only works on linux.
Yes, mealie is configured to properly forward external IP addresses into the `mealie.log` logfile. Note that due to restrictions in docker, IP address forwarding only works on Linux.
Your fail2ban usage should look like the following:
```
@@ -142,12 +136,12 @@ Use failregex line : ^ERROR:\s+Incorrect username or password from <HOST>
```
## Why An API?
An API allows integration into applications like [Home Assistant](https://www.home-assistant.io/) that can act as notification engines to provide custom notifications based of Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. Additionally, you can access nearly any backend service via the API giving you total control to extend the application. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation.
An API allows integration into applications like [Home Assistant](https://www.home-assistant.io/) that can act as notification engines to provide custom notifications based on Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. Additionally, you can access nearly any backend service via the API giving you total control to extend the application. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation.
## Why a Database?
Some users of static-site generator applications like ChowDown have expressed concerns about their data being stuck in a database. Considering this is a new project it is a valid concern to be worried about your data. Mealie specifically addresses this concern by provided automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in controls of how your data is represented** when exported from Mealie, which means you can easily migrate to any other service provided Mealie doesn't work for you.
Some users of static-site generator applications like ChowDown have expressed concerns about their data being stuck in a database. Considering this is a new project, it is a valid concern to be worried about your data. Mealie specifically addresses this concern by provided automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in control of how your data is represented** when exported from Mealie, which means you can easily migrate to any other service provided Mealie doesn't work for you.
As to why we need a database?
- **Developer Experience:** Without a database a lot of the work to maintain your data is taken on by the developer instead of a battle tested platform for storing data.
- **Multi User Support:** With a solid database as backend storage for your data Mealie can better support multi-user sites and avoid read/write access errors when multiple actions are taken at the same time.
- **Developer Experience:** Without a database, a lot of the work to maintain your data is taken on by the developer instead of a battle-tested platform for storing data.
- **Multi User Support:** With a solid database as backend storage for your data, Mealie can better support multi-user sites and avoid read/write access errors when multiple actions are taken at the same time.

View File

@@ -38,15 +38,15 @@ Categories are the overarching organizer for recipes. You can assign as many cat
#### Tags
Tags, are nearly identical to categories in function but play a secondary role in some cases. As such, we recommend that you use tags freely to help you organize your recipes by more specific topics. For example, if a recipe can be frozen or is a great left-over meal, you could assign the tags **frozen** and **left-over** and easily filter for those at a later time.
Tags are nearly identical to categories in function but play a secondary role in some cases. As such, we recommend that you use tags freely to help you organize your recipes by more specific topics. For example, if a recipe can be frozen or is a great left-over meal, you could assign the tags **frozen** and **left-over** and easily filter for those at a later time.
[Tags Demo](https://demo.mealie.io/g/home/recipes/tags){ .md-button .md-button--primary }
#### Tools
Tools, are another way that some users like to organize their recipes. If a recipe requires some specific equipment if can be helpful to assign the tools to the recipes. This is particularly useful for things that are less common, like a pressure cooker, or a sous vide.
Tools are another way that some users like to organize their recipes. If a recipe requires some specific equipment, it can be helpful to assign tools to the recipes. This is particularly useful for things that are less common, like a pressure cooker or a sous vide.
Each of the above organizers can be filtered in searches, and have their own pages where you can view all the recipes that are associated with those organizers.
Each of the above organizers can be filtered in searches and have their own pages where you can view all the recipes that are associated with those organizers.
[Tools Demo](https://demo.mealie.io/g/home/recipes/tools){ .md-button .md-button--primary }
@@ -64,7 +64,7 @@ Mealie also has the concept of cookbooks. These can be created inside of a group
## Meal Planning
Mealie uses a calendar like view to help you plan your meals. It shows you the previous day, and the next 6 days by default. You can toggle through the calendar by clicking the arrows on the top of the page. In editor mode, you can use the random recipe buttons, or manually add an entry.
Mealie uses a calendar like view to help you plan your meals. It shows you the previous day and the next 6 days by default. You can toggle through the calendar by clicking the arrows on the top of the page. In editor mode, you can use the random recipe buttons or manually add an entry.
!!! tip
You can also add a "Note" type entry to your meal-plan when you want to include something that might not have a specific recipes. This is great for leftovers, or for ordering out.
@@ -73,16 +73,16 @@ Mealie uses a calendar like view to help you plan your meals. It shows you the p
### Planner Rules
The meal planner has the concept of plan rules. These offer a flexible way to use your organizers to customize how a random recipe is inserted into your meal plan. You can set rules to restrict the pool of recipes based on the Tags and/or Categories of a recipe. Additionally, since meal plans have a Breakfast, Lunch, Dinner, and Snack labels you can specifically set a rule to be active for a **specific meal type** or even a **specific day of the week.**
The meal planner has the concept of plan rules. These offer a flexible way to use your organizers to customize how a random recipe is inserted into your meal plan. You can set rules to restrict the pool of recipes based on the Tags and/or Categories of a recipe. Additionally, since meal plans have a Breakfast, Lunch, Dinner, and Snack labels, you can specifically set a rule to be active for a **specific meal type** or even a **specific day of the week.**
[Planner Settings Demo](https://demo.mealie.io/group/mealplan/settings){ .md-button .md-button--primary }
## Shopping Lists
The shopping lists feature is a great way to keep track of what you need to buy for your next meal. You can add items directly to the shopping list, or link a recipe and all of it's ingredients to track meals during the week.
The shopping lists feature is a great way to keep track of what you need to buy for your next meal. You can add items directly to the shopping list or link a recipe and all of it's ingredients to track meals during the week.
!!! warning
At this time there isn't a tight integration between meal-plans and shopping lists, however it's something we have planned for the future.
At this time there isn't a tight integration between meal-plans and shopping lists; however, it's something we have planned for the future.
[Shopping List Demo](https://demo.mealie.io/shopping-lists){ .md-button .md-button--primary }
@@ -90,7 +90,7 @@ The shopping lists feature is a great way to keep track of what you need to buy
## Data Management
Managing a robust collection of recipes inevitable requires a lot of data. Mealie has a robust data management system that allows you to easily some of the more important data sets in your collection. Here's some of the features that are available in the `group/data/<type>` pages:
Managing a robust collection of recipes inevitable requires a lot of data. Mealie has a robust data management system that allows you to easily export some of the more important data sets in your collection. Here's some of the features that are available in the `group/data/<type>` pages:
- Recipes
- Bulk Actions
@@ -113,7 +113,7 @@ Managing a robust collection of recipes inevitable requires a lot of data. Meali
### Site Settings
The site settings page contains general information about your installation like the application version, some configuration details, and some utilities to help you confirm your installation is working as expected. For example, you can use the Email Configuration section to validate that your email credentials are setup correctly and that the email service is working as expected. Additionally, there is a docker-volume utility that will confirm your volumes are configured and shared correctly between the front and backend of the application.
The site settings page contains general information about your installation like the application version, some configuration details, and some utilities to help you confirm your installation is working as expected. For example, you can use the Email Configuration section to validate that your email credentials are set up correctly and that the email service is working as expected. Additionally, there is a docker-volume utility that will confirm your volumes are configured and shared correctly between the front and backend of the application.
[Settings Demo](https://demo.mealie.io/admin/site-settings){ .md-button .md-button--primary }
@@ -125,7 +125,7 @@ There is a small management area for users and groups that allows you to create,
### Backups
The backups page provides a full system backup of your installation including all assets and images related to recipes. These are archived into a zip file and stored on the server but can also be downloaded through the UI. Due to some issues in the past Mealie no longer performs automatic backups, **it is advised that during setup you also setup a backup strategy to ensure your data is not lost.**
The backups page provides a full system backup of your installation including all assets and images related to recipes. These are archived into a zip file and stored on the server but can also be downloaded through the UI. Due to some issues in the past, Mealie no longer performs automatic backups; **it is advised that during setup you also set up a backup strategy to ensure your data is not lost.**
[Backups Demo](https://demo.mealie.io/admin/backups){ .md-button .md-button--primary }

View File

@@ -1,23 +1,23 @@
# Installation Checklist
To install Mealie on your server there are a few steps for proper configuration. Let's go through them.
To install Mealie on your server, there are a few steps for proper configuration. Let's go through them.
!!! tip TLDR
Don't need step by step? Checkout the
Don't need step-by-step? Check out:
- [SQLite docker-compose](./sqlite.md)
- [Postgres docker-compose](./postgres.md)
## Pre-work
To deploy mealie on your local network it is highly recommended to use docker to deploy the image straight from dockerhub. Using the docker-compose templates provided, you should be able to get a stack up and running easily by changing a few default values and deploying. You can deploy with either SQLite (default) or Postgres. SQLite is sufficient for most use cases. Additionally, with Mealie's automated backup and restore functionality, you can easily move between SQLite and Postgres as you wish.
To deploy mealie on your local network, it is highly recommended to use Docker to deploy the image straight from the GitHub registry. Using the docker-compose templates provided, you should be able to get a stack up and running easily by changing a few default values and deploying. You can deploy with either SQLite (default) or Postgres. SQLite is sufficient for most use cases. Additionally, with Mealie's automated backup and restore functionality, you can easily move between SQLite and Postgres as you wish.
[Get Docker](https://docs.docker.com/get-docker/)
[Get Docker Compose](https://docs.docker.com/compose/install/)
[Mealie on Dockerhub](https://hub.docker.com/r/hkotel/mealie)
[Mealie on GitHub registry](https://github.com/mealie-recipes/mealie/pkgs/container/mealie)
- linux/amd64
- linux/arm64
@@ -28,7 +28,7 @@ To deploy mealie on your local network it is highly recommended to use docker to
## Migrating From Other V1 Versions
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do:
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do:
1. Take a backup just in case!
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v1.0.0-RC1.1`
@@ -50,13 +50,13 @@ You can find the relevant ready to use docker-compose files for supported instal
## Step 2: Setting up your files.
The following steps were tested on a Ubuntu 20.04 server, but should work for most other Linux distributions. These steps are not required, but is how I generally will setup services on my server.
The following steps were tested on a Ubuntu 20.04 server, but should work for most other Linux distributions. These steps are not required, but this is how I generally will setup services on my server.
1. SSH into your server and navigate to the home directory of the user you want to run Mealie as. If that is your current user, you can use `cd ~` to ensure you're in the right directory.
2. Create a directory called `docker` and navigate into it: `mkdir docker && cd docker` (this is optional, if you organizer your docker installs separate from everything else)
2. Create a directory called `docker` and navigate into it: `mkdir docker && cd docker` (this is optional, if you organize your docker installs separate from everything else)
3. Do the same for mealie: `mkdir mealie && cd mealie`
4. Create a docker-compose.yaml file in the mealie directory: `touch docker-compose.yaml`
5. Use the text editor or your choice to edit the file and copy the contents of the docker-compose template for the deployment type you want to use: `nano docker-compose.yaml` or `vi docker-compose.yaml`
5. Use the text editor of your choice to edit the file and copy the contents of the docker-compose template for the deployment type you want to use: `nano docker-compose.yaml` or `vi docker-compose.yaml`
## Step 2: Customizing The `docker-compose.yaml` files.
@@ -69,10 +69,10 @@ After you've decided setup the files it's important to set a few ENV variables t
## Step 3: Startup
After you've configured your database, and updated the `docker-compose.yaml` files, you can start Mealie by running the following command in the directory where you've added your `docker-compose.yaml`.
After you've configured your database and updated the `docker-compose.yaml` files, you can start Mealie by running the following command in the directory where you've added your `docker-compose.yaml`.
```bash
$ docker-compose up -d
$ docker compose up -d
```
You should see the containers start up without error. You should now be able to access the Mealie frontend at [http://localhost:9925](http://localhost:9925).
@@ -89,13 +89,13 @@ You should see the containers start up without error. You should now be able to
## Step 4: Validate Installation
After the startup is complete you should see a login screen. Use the default credentials above to login and navigate to `/admin/site-settings`. Here you'll find a summary of your configuration details and their respective status. Before proceeding you should validate that the configuration is correct. For any warnings or errors the page will display an error and notify you of what you need to verify.
After the startup is complete, you should see a login screen. Use the default credentials above to log in and navigate to `/admin/site-settings`. Here, you'll find a summary of your configuration details and their respective status. Before proceeding, you should validate that the configuration is correct. For any warnings or errors the page will display an error and notify you of what you need to verify.
## Step 5: Backup
While v1.0.0 is a great step to data-stability and security, it's not a backup. Mealie provides a full site data backup mechanism through the UI.
These backups are just plain .zip files that you can download from the UI or access via the mounted volume on your system. For complete data protection you MUST store these backups somewhere safe, and outside of the server where they are deployed.
These backups are just plain .zip files that you can download from the UI or access via the mounted volume on your system. For complete data protection you MUST store these backups somewhere safe, outside of the server where they are deployed.
## Appendix
@@ -107,13 +107,11 @@ See all available tags on [GitHub](https://github.com/mealie-recipes/mealie/pkgs
The nightly build are the latest and greatest builds that are built directly off of every commit to the `mealie-next` branch and as such may contain bugs. These are great to help the community catch bugs before they hit the stable release or if you like living on the edge.
`ghrc.io/mealie-recipes/mealie:<version>`
`ghcr.io/mealie-recipes/mealie:<version>`
We also provide versioned containers that allow to pin to a specific release. Each time a new release is built a new tag will be pushed with the version. These are great to pin to a specific version and allows you to have absolute control on when you upgrade your container.
`ghrc.io/mealie-recipes/mealie:latest`
_Note: This tag is not yet available, it will be available with the v1 stable release_
`ghcr.io/mealie-recipes/mealie:latest`
The latest tag provides the latest released image of Mealie.

View File

@@ -2,23 +2,21 @@
PostgreSQL might be considered if you need to support many concurrent users. In addition, some features are only enabled on PostgreSQL, such as fuzzy search.
**For Environmental Variable Configuration See:**
- [Configuration](./backend-config.md)
**For Environment Variable Configuration, see** [Backend Configuration](./backend-config.md)
```yaml
---
version: "3.7"
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.0.0-RC1.1
image: ghcr.io/mealie-recipes/mealie:v1.0.0 # (3)
container_name: mealie
ports:
- "9925:9000"
- "9925:9000" # (1)
deploy:
resources:
limits:
memory: 1000M # (1)
memory: 1000M # (2)
depends_on:
- postgres
volumes:
@@ -46,7 +44,7 @@ services:
image: postgres:15
restart: always
volumes:
- ./mealie-pgdata:/var/lib/postgresql/data
- mealie-pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: mealie
POSTGRES_USER: mealie
@@ -62,3 +60,4 @@ volumes:
1. To access the mealie interface you only need to expose port 9000 on the mealie container. Here we expose port 9925 on the host, but feel free to change this to any port you like.
2. Setting an explicit memory limit is recommended. Python can pre-allocate larger amounts of memory than is necessary if you have a machine with a lot of RAM. This can cause the container to idle at a high memory usage. Setting a memory limit will improve idle performance.
3. Whilst a 'latest' tag is available, the Mealie team advises specifying a specific version tag and consciously updating to newer versions when you have time to read the release notes and ensure you follow any manual actions required (which should be rare).

View File

@@ -1,17 +1,19 @@
# Installing with SQLite
!!! Warning
If you're planning on deploying and using Network Attached Storage with Mealie, you should use [Postgres](./postgres.md) instead of SQLite. SQLite is not designed to be used with Network Attached Storage and can cause data corruption, or locked database errors
SQLite is a popular, open source, self-contained, zero-configuration database that is the ideal choice for Mealie when you have 1-20 Users. Below is a ready to use docker-compose.yaml file for deploying Mealie on your server.
**For Environmental Variable Configuration See:**
- [Configuration](./backend-config.md)
**For Environment Variable Configuration, see** [Backend Configuration](./backend-config.md)
```yaml
---
version: "3.7"
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.0.0-RC1.1
image: ghcr.io/mealie-recipes/mealie:v1.0.0 # (3)
container_name: mealie
ports:
- "9925:9000" # (1)
@@ -41,3 +43,4 @@ volumes:
1. To access the mealie interface you only need to expose port 9000 on the container. Here we expose port 9925 on the host, but feel free to change this to any port you like.
2. Setting an explicit memory limit is recommended. Python can pre-allocate larger amounts of memory than is necessary if you have a machine with a lot of RAM. This can cause the container to idle at a high memory usage. Setting a memory limit will improve idle performance.
3. Whilst a 'latest' tag is available, the Mealie team advises specifying a specific version tag and consciously updating to newer versions when you have time to read the release notes and ensure you follow any manual actions required (which should be rare).

View File

@@ -1,11 +1,5 @@
# About The Project
!!! warning "Mealie v1 Beta Release"
This documentation is for the Mealie v1 Beta release and is not final. As such, it may contain incomplete or incorrect information. You should understand that installing Mealie v1 Beta is a work in progress and while we've committed to maintaining the database schema and provided migrations, we are still in the process of adding new features, and robust testing to ensure the application works as expected.
You should likely find bugs, errors, and unfinished pages within the application. To find the current status of the release you can checkout the [project on github](https://github.com/hay-kot/mealie/projects/7) or reach out on discord.
Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications.
[Remember to join the Discord](https://discord.gg/QuStdQGSGK)

View File

@@ -1,9 +1,9 @@
# Migrating to Mealie v1 Release
The version 1 release of Mealie should be seen as an entirely different application. A whole host of changes have been made to improve the application, performance, and developer experience. Most of these improvements required significant breaking changes in the application that made a clean and easy migration impossible. However, if you've used Mealie prior to v1 there is a migration path to get most of your data from the old version to the new v1 version.
The version 1 release of Mealie should be seen as an entirely different application. A whole host of changes have been made to improve the application, performance, and developer experience. Most of these improvements required significant breaking changes in the application that made a clean and easy migration impossible. However, if you've used Mealie prior to v1, there is a migration path to get most of your data from the old version to the new v1 version.
!!! info "Currently Supported Migration Data"
Supporting more data is a work in progress, but not a current priority. I'm open to PR's to add support for additional data.
Supporting more data is a work in progress, but not a current priority. I'm open to PRs to add support for additional data.
- [x] Recipes
- [x] Categories
@@ -16,19 +16,15 @@ The version 1 release of Mealie should be seen as an entirely different applicat
## Migration Considerations
Before you migrate to v1.0.0-beta-x please consider the following:
Before you migrate to v1.0.0 please consider the following:
**API Integration Will Break**
Several of the endpoints in the API have changed. This means that you will need to update your code to use the new endpoints.
**Meal Plan Notifications Are Not Yet Implemented**
**Recipes Are Private By Default**
If you're using the Meal Plan webhook feature it has yet to be implemented in v1. This feature is being significantly improved in v1 and has yet to be fully fleshed out. If you were a heavy user, you may want to wait until v1 to use this feature.
**Recipes are Now Private**
This can be a plus or a minus depending on your use case. If you relied on the old implementation that allowed viewing of recipes without logging in, you will loose that access. We are planning on implementing a public facing interface for groups/tenants to allow unauthenticated users to view public recipes.
By default, recipes can only be viewed by logged-in users. You can fine-tune public recipe access, or keep your instance fully private. For more information, check out the [Permissions and Public Access guide](../getting-started/usage/permissions-and-public-access.md).
## Step 1: Setting Up The New Application
@@ -37,7 +33,9 @@ Given the nature of the upgrade, it is highly recommended that you stand up a ne
## Step 2: Exporting Your Data from Pre-v1
In your instance of Mealie prior to v1, perform an export of your data in the Admin section. Be sure to include the recipes when performing the export. Checking additional items won't impact the migration, but they will be ignored if they are included.
In your instance of Mealie prior to v1, perform an export (backup) of your data in the Admin section. Be sure to include the recipes when performing the export. Checking additional items won't impact the migration, but they will be ignored if they are included. The backups section is located on the admin dashboard in the section labeled "Backups":
![pre-v1-backup-location-image](../../assets/img/pre-v1-backup-location.png)
## Step 3: Using the Migration Tool
@@ -47,11 +45,11 @@ In your new v1 instance, navigate to `/group/migrations` and select "Mealie" fro
In most cases, it's faster to manually migrate the recipes that didn't take instead of trying to identify why the recipes failed to import. If you're experiencing issues with the migration tool, please open an issue on GitHub.
!!! note "Recipe Owners"
When perform any migration, it will automatically assign the owner of the recipe to the user that performed the migration. All group members will still be able to access the recipe, however the owner has special permissions to lock the recipe from edits from other users.
When perform any migration, it will automatically assign the owner of the recipe to the user that performed the migration. All group members will still be able to access the recipe; however, the owner has special permissions to lock the recipe from edits from other users.
## Step 4: Reviewing New Features
v1 Comes with a whole host of new features and improvements. Checkout the changelog to get a sense for what's new.
v1 Comes with a whole host of new features and improvements. Check out the changelog to get a sense for what's new.
- [Github releases changelog](https://github.com/hay-kot/mealie/releases)
- [Github releases changelog](https://github.com/mealie-recipes/mealie/releases)

View File

@@ -19,6 +19,7 @@ If you are upgrading from pre-v1.0.0 to v1.0.0, make sure you read [Migrating to
## Docker
For all setups using Docker the updating process looks something like this
- Stop the container using docker-compose down
- Pull the latest image using `docker-compose pull`
- Start the container again using `docker-compose up -d`
- Stop the container using `docker compose down`
- If you are not using the latest tag, change the version (image tag) in your docker-compose file
- Pull the latest image using `docker compose pull`
- Start the container again using `docker compose up -d`

View File

@@ -34,4 +34,4 @@ ALTER USER mealie WITH SUPERUSER;
ALTER USER mealie WITH NOSUPERUSER;
```
For more information see [GitHub Issue #1500](https://github.com/hay-kot/mealie/issues/1500)
For more information see [GitHub Issue #1500](https://github.com/mealie-recipes/mealie/issues/1500)

View File

@@ -0,0 +1,57 @@
# Permissions and Public Access
Mealie provides various levels of user access and permissions. This includes:
- Authentication and registration ([check out the LDAP guide](./ldap.md) for how to configure access using LDAP)
- Customizable user permissions
- Fine-tuned public access for non-users
## Customizable User Permissions
Each user can be configured to have varying levels of access. Some of these permissions include:
- Access to Administrator tools
- Access to inviting other users
- Access to manage their group and group data
Administrators can navigate to the Settings page and access the User Management page to configure these settings.
[User Management Demo](https://demo.mealie.io/admin/manage/users){ .md-button .md-button--primary }
## Public Recipe Access
By default, groups are set to private, meaning only logged-in users may access the group. In order for a recipe to be viewable by public (not logged-in) users, two criteria must be met:
1. The group must not be private, *and* the group setting for allowing users outside of your group to see your recipes must be enabled. These can be toggled on the Group Settings page
2. The recipe must be set to public. This can be toggled for each recipe individually, or in bulk using the Recipe Data Management page
Additionally, if the group is not private, public users can view all public group data (public recipes, public cookbooks, etc.) from the home page ([e.g. the demo home page](https://demo.mealie.io/g/home)).
[Group Settings Demo](https://demo.mealie.io/group){ .md-button .md-button--primary }
More broadly, here are the rules for how recipe access is determined:
- Private links that are generated from the recipe page using the `Share` button bypass all group and recipe permissions
- Private groups block all access to recipes, including those that are public, except as noted above.
- Groups with "Allow users outside of your group to see your recipes" disabled block all access to recipes, except as noted above.
- Private recipes block all access to the recipe from public links. This does not affect Private Links.
```mermaid
stateDiagram-v2
r1: Request Access
p1: Using Private Link?
p2: Is Group Private?
p3: Is Recipe Private?
s1: Deny Access
n1: Allow Access
r1 --> p1
p1 --> p2: No
p1 --> n1: Yes
p2 --> s1: Yes
p2 --> p3: No
p3 --> s1: Yes
p3 --> n1: No
```

File diff suppressed because one or more lines are too long

View File

@@ -102,7 +102,6 @@ const SAVE_EVENT = "save";
const DELETE_EVENT = "delete";
const CLOSE_EVENT = "close";
const JSON_EVENT = "json";
const OCR_EVENT = "ocr";
export default defineComponent({
components: { RecipeContextMenu, RecipeFavoriteBadge, RecipeTimerMenu, RecipeTimelineBadge },
@@ -139,12 +138,8 @@ export default defineComponent({
type: Boolean,
default: false,
},
showOcrButton: {
type: Boolean,
default: false,
},
},
setup(props, context) {
setup(_, context) {
const deleteDialog = ref(false);
const { i18n, $globals } = useContext();
@@ -175,15 +170,6 @@ export default defineComponent({
},
];
if (props.showOcrButton) {
editorButtons.splice(2, 0, {
text: i18n.t("ocr-editor.ocr-editor"),
icon: $globals.icons.eye,
event: OCR_EVENT,
color: "accent",
});
}
function emitHandler(event: string) {
switch (event) {
case CLOSE_EVENT:

View File

@@ -26,54 +26,69 @@
>
<div style="max-height: 70vh; overflow-y: auto">
<v-card
v-for="(section, sectionIndex) in recipeIngredientSections" :key="section.recipeId + sectionIndex"
v-for="(recipeSection, recipeSectionIndex) in recipeIngredientSections" :key="recipeSection.recipeId + recipeSectionIndex"
elevation="0"
height="fit-content"
width="100%"
>
<v-divider v-if="sectionIndex > 0" class="mt-3" />
<v-divider v-if="recipeSectionIndex > 0" class="mt-3" />
<v-card-title
v-if="recipeIngredientSections.length > 1"
class="justify-center"
class="justify-center text-h5"
width="100%"
>
<v-container style="width: 100%;">
<v-row no-gutters class="ma-0 pa-0">
<v-col cols="12" align-self="center" class="text-center">
{{ section.recipeName }}
{{ recipeSection.recipeName }}
</v-col>
</v-row>
<v-row v-if="section.recipeScale > 1" no-gutters class="ma-0 pa-0">
<v-row v-if="recipeSection.recipeScale > 1" no-gutters class="ma-0 pa-0">
<!-- TODO: make this editable in the dialog and visible on single-recipe lists -->
<v-col cols="12" align-self="center" class="text-center">
({{ $tc("recipe.quantity") }}: {{ section.recipeScale }})
({{ $tc("recipe.quantity") }}: {{ recipeSection.recipeScale }})
</v-col>
</v-row>
</v-container>
</v-card-title>
<div
:class="$vuetify.breakpoint.smAndDown ? '' : 'ingredient-grid'"
:style="$vuetify.breakpoint.smAndDown ? '' : { gridTemplateRows: `repeat(${Math.ceil(section.ingredients.length / 2)}, min-content)` }"
>
<v-list-item
v-for="(ingredientData, i) in section.ingredients"
:key="'ingredient' + i"
dense
@click="recipeIngredientSections[sectionIndex].ingredients[i].checked = !recipeIngredientSections[sectionIndex].ingredients[i].checked"
<div>
<div
v-for="(ingredientSection, ingredientSectionIndex) in recipeSection.ingredientSections"
:key="recipeSection.recipeId + recipeSectionIndex + ingredientSectionIndex"
>
<v-checkbox
hide-details
:input-value="ingredientData.checked"
class="pt-0 my-auto py-auto"
color="secondary"
/>
<v-list-item-content :key="ingredientData.ingredient.quantity">
<RecipeIngredientListItem
:ingredient="ingredientData.ingredient"
:disable-amount="ingredientData.disableAmount"
:scale="section.recipeScale" />
</v-list-item-content>
</v-list-item>
<v-card-title v-if="ingredientSection.sectionName" class="ingredient-title mt-2 pb-0 text-h6">
{{ ingredientSection.sectionName }}
</v-card-title>
<div
:class="$vuetify.breakpoint.smAndDown ? '' : 'ingredient-grid'"
:style="$vuetify.breakpoint.smAndDown ? '' : { gridTemplateRows: `repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)` }"
>
<v-list-item
v-for="(ingredientData, i) in ingredientSection.ingredients"
:key="recipeSection.recipeId + recipeSectionIndex + ingredientSectionIndex + i"
dense
@click="recipeIngredientSections[recipeSectionIndex]
.ingredientSections[ingredientSectionIndex]
.ingredients[i].checked = !recipeIngredientSections[recipeSectionIndex]
.ingredientSections[ingredientSectionIndex]
.ingredients[i]
.checked"
>
<v-checkbox
hide-details
:input-value="ingredientData.checked"
class="pt-0 my-auto py-auto"
color="secondary"
/>
<v-list-item-content :key="ingredientData.ingredient.quantity">
<RecipeIngredientListItem
:ingredient="ingredientData.ingredient"
:disable-amount="ingredientData.disableAmount"
:scale="recipeSection.recipeScale" />
</v-list-item-content>
</v-list-item>
</div>
</div>
</div>
</v-card>
</div>
@@ -112,17 +127,22 @@ export interface RecipeWithScale extends Recipe {
scale: number;
}
export interface ShoppingListRecipeIngredient {
export interface ShoppingListIngredient {
checked: boolean;
ingredient: RecipeIngredient;
disableAmount: boolean;
}
export interface ShoppingListIngredientSection {
sectionName: string;
ingredients: ShoppingListIngredient[];
}
export interface ShoppingListRecipeIngredientSection {
recipeId: string;
recipeName: string;
recipeScale: number;
ingredients: ShoppingListRecipeIngredient[];
ingredientSections: ShoppingListIngredientSection[];
}
export default defineComponent({
@@ -191,7 +211,7 @@ export default defineComponent({
continue;
}
const shoppingListIngredients: ShoppingListRecipeIngredient[] = recipe.recipeIngredient.map((ing) => {
const shoppingListIngredients: ShoppingListIngredient[] = recipe.recipeIngredient.map((ing) => {
return {
checked: true,
ingredient: ing,
@@ -199,11 +219,35 @@ export default defineComponent({
}
});
const shoppingListIngredientSections = shoppingListIngredients.reduce((sections, ing) => {
// if title append new section to the end of the array
if (ing.ingredient.title) {
sections.push({
sectionName: ing.ingredient.title,
ingredients: [ing],
});
return sections;
}
// append new section if first
if (sections.length === 0) {
sections.push({
sectionName: "",
ingredients: [ing],
});
return sections;
}
// otherwise add ingredient to last section in the array
sections[sections.length - 1].ingredients.push(ing);
return sections;
}, [] as ShoppingListIngredientSection[]);
recipeSectionMap.set(recipe.slug, {
recipeId: recipe.id,
recipeName: recipe.name,
recipeScale: recipe.scale,
ingredients: shoppingListIngredients,
ingredientSections: shoppingListIngredientSections,
})
}
@@ -231,9 +275,11 @@ export default defineComponent({
}
function bulkCheckIngredients(value = true) {
recipeIngredientSections.value.forEach((section) => {
section.ingredients.forEach((ing) => {
ing.checked = value;
recipeIngredientSections.value.forEach((recipeSection) => {
recipeSection.ingredientSections.forEach((ingSection) => {
ingSection.ingredients.forEach((ing) => {
ing.checked = value;
});
});
});
}
@@ -246,10 +292,12 @@ export default defineComponent({
}
const ingredients: RecipeIngredient[] = [];
section.ingredients.forEach((ing) => {
if (ing.checked) {
ingredients.push(ing.ingredient);
}
section.ingredientSections.forEach((ingSection) => {
ingSection.ingredients.forEach((ing) => {
if (ing.checked) {
ingredients.push(ing.ingredient);
}
});
});
if (!ingredients.length) {
@@ -272,7 +320,11 @@ export default defineComponent({
}
})
success ? alert.success(i18n.t("recipe.recipes-added-to-list") as string)
const successMessage = promises.length === 1
? i18n.t("recipe.successfully-added-to-list") as string
: i18n.t("recipe.failed-to-add-to-list") as string;
success ? alert.success(successMessage)
: alert.error(i18n.t("failed-to-add-recipes-to-list") as string)
state.shoppingListDialog = false;

View File

@@ -143,7 +143,7 @@ export default defineComponent({
}
const { share, isSupported: shareIsSupported } = useShare();
const { copy } = useClipboard();
const { copy, copied, isSupported } = useClipboard();
function getRecipeText() {
return i18n.t("recipe.share-recipe-message", [props.name]);
@@ -154,8 +154,18 @@ export default defineComponent({
}
async function copyTokenLink(token: string) {
await copy(getTokenLink(token));
alert.success(i18n.t("recipe-share.recipe-link-copied-message") as string);
if (isSupported.value) {
await copy(getTokenLink(token));
if (copied.value) {
alert.success(i18n.t("recipe-share.recipe-link-copied-message") as string);
}
else {
alert.error(i18n.t("general.clipboard-copy-failure") as string);
}
}
else {
alert.error(i18n.t("general.clipboard-not-supported") as string);
}
}
async function shareRecipe(token: string) {

View File

@@ -52,11 +52,20 @@ export default defineComponent({
});
const ingredientCopyText = computed(() => {
return props.value
.map((ingredient) => {
return `${parseIngredientText(ingredient, props.disableAmount, props.scale, false)}`;
})
.join("\n");
const components: string[] = [];
props.value.forEach((ingredient) => {
if (ingredient.title) {
if (components.length) {
components.push("");
}
components.push(`[${ingredient.title}]`);
}
components.push(parseIngredientText(ingredient, props.disableAmount, props.scale, false));
});
return components.join("\n");
});
function toggleChecked(index: number) {

View File

@@ -1,7 +1,7 @@
<template>
<div v-if="value.length > 0 || edit" class="mt-8">
<h2 class="my-4">{{ $t("recipe.note") }}</h2>
<div v-for="(note, index) in value" :key="'note' + index" class="mt-1">
<div v-for="(note, index) in value" :id="'note' + index" :key="'note' + index" class="mt-1">
<v-card v-if="edit">
<v-card-text>
<div class="d-flex align-center">

View File

@@ -1,390 +0,0 @@
<template>
<v-container
v-if="recipe && recipe.slug && recipe.settings && recipe.recipeIngredient"
:class="{
'pa-0': $vuetify.breakpoint.smAndDown,
}"
>
<BannerExperimental />
<div v-if="loading">
<v-spacer />
<v-progress-circular indeterminate class="" color="primary"> </v-progress-circular>
{{ loadingText }}
<v-spacer />
</div>
<v-row v-if="!loading">
<v-col cols="12" sm="7" md="7" lg="7">
<RecipeOcrEditorPageCanvas
:image="canvasImage"
:tsv="tsv"
@setText="canvasSetText"
@update-recipe="updateRecipe"
@close-editor="closeEditor"
@text-selected="updateSelectedText"
>
</RecipeOcrEditorPageCanvas>
<RecipeOcrEditorPageHelp />
</v-col>
<v-col cols="12" sm="5" md="5" lg="5">
<v-tabs v-model="tab" fixed-tabs>
<v-tab key="header">
{{ $t("general.recipe") }}
</v-tab>
<v-tab key="ingredients">
{{ $t("recipe.ingredients") }}
</v-tab>
<v-tab key="instructions">
{{ $t("recipe.instructions") }}
</v-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item key="header">
<v-text-field
v-model="recipe.name"
class="my-3"
:label="$t('recipe.recipe-name')"
:rules="[validators.required]"
@focus="selectedRecipeField = 'name'"
>
</v-text-field>
<div class="d-flex flex-wrap">
<v-text-field
v-model="recipe.totalTime"
class="mx-2"
:label="$t('recipe.total-time')"
@click="selectedRecipeField = 'totalTime'"
></v-text-field>
<v-text-field
v-model="recipe.prepTime"
class="mx-2"
:label="$t('recipe.prep-time')"
@click="selectedRecipeField = 'prepTime'"
></v-text-field>
<v-text-field
v-model="recipe.performTime"
class="mx-2"
:label="$t('recipe.perform-time')"
@click="selectedRecipeField = 'performTime'"
></v-text-field>
</div>
<v-textarea
v-model="recipe.description"
auto-grow
min-height="100"
:label="$t('recipe.description')"
@click="selectedRecipeField = 'description'"
>
</v-textarea>
<v-text-field
v-model="recipe.recipeYield"
dense
:label="$t('recipe.servings')"
@click="selectedRecipeField = 'recipeYield'"
>
</v-text-field>
</v-tab-item>
<v-tab-item key="ingredients">
<div class="d-flex justify-end mt-2">
<RecipeDialogBulkAdd class="ml-1 mr-1" :input-text-prop="canvasSelectedText" @bulk-data="addIngredient" />
<BaseButton @click="addIngredient"> {{ $t("general.new") }} </BaseButton>
</div>
<draggable
v-if="recipe.recipeIngredient.length > 0"
v-model="recipe.recipeIngredient"
handle=".handle"
v-bind="{
animation: 200,
group: 'description',
disabled: false,
ghostClass: 'ghost',
}"
@start="drag = true"
@end="drag = false"
>
<TransitionGroup type="transition" :name="!drag ? 'flip-list' : ''">
<RecipeIngredientEditor
v-for="(ingredient, index) in recipe.recipeIngredient"
:key="ingredient.referenceId"
v-model="recipe.recipeIngredient[index]"
class="list-group-item"
:disable-amount="recipe.settings.disableAmount"
@delete="recipe.recipeIngredient.splice(index, 1)"
@clickIngredientField="setSingleIngredient($event, index)"
/>
</TransitionGroup>
</draggable>
</v-tab-item>
<v-tab-item key="instructions">
<div class="d-flex justify-end mt-2">
<RecipeDialogBulkAdd class="ml-1 mr-1" :input-text-prop="canvasSelectedText" @bulk-data="addStep" />
<BaseButton @click="addStep()"> {{ $t("general.new") }}</BaseButton>
</div>
<RecipePageInstructions
v-model="recipe.recipeInstructions"
:ingredients="recipe.recipeIngredient"
:disable-amount="recipe.settings.disableAmount"
:edit="true"
:recipe="recipe"
:assets.sync="recipe.assets"
@click-instruction-field="setSingleStep"
/>
</v-tab-item>
</v-tabs-items>
</v-col>
</v-row>
</v-container>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, reactive, toRefs, useContext, useRouter, computed, useRoute } from "@nuxtjs/composition-api";
import { until } from "@vueuse/core";
import { invoke } from "@vueuse/shared";
import draggable from "vuedraggable";
import RecipePageInstructions from "~/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageInstructions.vue";
import { useUserApi, useStaticRoutes } from "~/composables/api";
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/lib/api/types/ocr";
import { validators } from "~/composables/use-validators";
import { Recipe, RecipeIngredient, RecipeStep } from "~/lib/api/types/recipe";
import { Paths, Leaves, SelectedRecipeLeaves } from "~/types/ocr-types";
import BannerExperimental from "~/components/global/BannerExperimental.vue";
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
import RecipeOcrEditorPageCanvas from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageCanvas.vue";
import RecipeOcrEditorPageHelp from "~/components/Domain/Recipe/RecipeOcrEditorPage/RecipeOcrEditorPageParts/RecipeOcrEditorPageHelp.vue";
import { uuid4 } from "~/composables/use-utils";
import { NoUndefinedField } from "~/lib/api/types/non-generated";
// Temporary Shim until we have a better solution
// https://github.com/phillipdupuis/pydantic-to-typescript/issues/28
type OcrTsvResponse = NoUndefinedField<NullableOcrTsvResponse>;
export default defineComponent({
components: {
RecipeIngredientEditor,
draggable,
BannerExperimental,
RecipeDialogBulkAdd,
RecipePageInstructions,
RecipeOcrEditorPageCanvas,
RecipeOcrEditorPageHelp,
},
props: {
recipe: {
type: Object as () => NoUndefinedField<Recipe>,
required: true,
},
},
setup(props) {
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
const api = useUserApi();
const tsv = ref<OcrTsvResponse[]>([]);
const drag = ref(false);
const { i18n } = useContext();
const { recipeAssetPath } = useStaticRoutes();
function assetURL(assetName: string) {
return recipeAssetPath(props.recipe.id, assetName);
}
const state = reactive({
loading: true,
loadingText: i18n.tc("general.loading-recipe"),
tab: null,
selectedRecipeField: "" as SelectedRecipeLeaves | "",
canvasSelectedText: "",
canvasImage: new Image(),
});
const setPropertyValueByPath = function <T extends Recipe>(object: T, path: Paths<T>, value: any) {
const a = path.split(".");
let nextProperty: any = object;
for (let i = 0, n = a.length - 1; i < n; ++i) {
const k = a[i];
if (k in nextProperty) {
nextProperty = nextProperty[k];
} else {
return;
}
}
nextProperty[a[a.length - 1]] = value;
};
/**
* This function will find the title of a recipe with the assumption that the title
* has the biggest ratio of surface area / number of words on the image.
* @return Returns the text parts of the block with the highest score.
*/
function findRecipeTitle() {
const filtered = tsv.value.filter((element) => element.level === 2 || element.level === 5);
const blocks = [[]] as OcrTsvResponse[][];
let blockNum = 1;
filtered.forEach((element, index, array) => {
if (index !== 0 && array[index - 1].blockNum !== element.blockNum) {
blocks.push([]);
blockNum = element.blockNum;
}
blocks[blockNum - 1].push(element);
});
let bestScore = 0;
let bestBlock = blocks[0];
blocks.forEach((element) => {
// element[0] is the block declaration line containing the blocks total dimensions
// element.length is the number of words (+ 2) contained in that block
const elementScore = (element[0].height * element[0].width) / element.length; // Prettier is adding useless parenthesis for a mysterious reason
const elementText = element.map((element) => element.text).join(""); // Identify empty blocks and don't count them
if (elementScore > bestScore && elementText !== "") {
bestBlock = element;
bestScore = elementScore;
}
});
return bestBlock
.filter((element) => element.level === 5 && element.conf >= 40)
.map((element) => {
return element.text.trim();
})
.join(" ");
}
onMounted(() => {
invoke(async () => {
await until(props.recipe).not.toBeNull();
state.loadingText = i18n.tc("general.loading-ocr-data");
const assetName = props.recipe.assets[0].fileName;
const imagesrc = assetURL(assetName);
state.canvasImage.src = imagesrc;
const res = await api.ocr.assetToTsv(props.recipe.slug, assetName);
tsv.value = res.data as OcrTsvResponse[];
state.loading = false;
if (props.recipe.name.match(/New\sOCR\sRecipe(\s\([0-9]+\))?/g)) {
props.recipe.name = findRecipeTitle();
}
});
});
function addIngredient(ingredients: Array<string> | null = null) {
if (ingredients?.length) {
const newIngredients = ingredients.map((x) => {
return {
referenceId: uuid4(),
title: "",
note: x,
unit: undefined,
food: undefined,
disableAmount: true,
quantity: 1,
originalText: "",
};
});
if (newIngredients) {
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
props.recipe.recipeIngredient.push(...newIngredients);
}
} else {
props.recipe.recipeIngredient.push({
referenceId: uuid4(),
title: "",
note: "",
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
unit: undefined,
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
food: undefined,
disableAmount: true,
quantity: 1,
});
}
}
function addStep(steps: Array<string> | null = null) {
if (!props.recipe.recipeInstructions) {
return;
}
if (steps) {
const cleanedSteps = steps.map((step) => {
return { id: uuid4(), text: step, title: "", ingredientReferences: [] };
});
props.recipe.recipeInstructions.push(...cleanedSteps);
} else {
props.recipe.recipeInstructions.push({ id: uuid4(), text: "", title: "", ingredientReferences: [] });
}
}
// EVENT HANDLERS
// Canvas component event handlers
async function updateRecipe() {
const { data } = await api.recipes.updateOne(props.recipe.slug, props.recipe);
if (data?.slug) {
router.push(`/g/${groupSlug.value}/r/${data.slug}`);
}
}
function closeEditor() {
router.push(`/g/${groupSlug.value}/r/${props.recipe.slug}`);
}
const canvasSetText = function () {
if (state.selectedRecipeField !== "") {
setPropertyValueByPath<Recipe>(props.recipe, state.selectedRecipeField, state.canvasSelectedText);
}
};
function updateSelectedText(value: string) {
state.canvasSelectedText = value;
}
// Recipe field selection event handlers
function setSingleIngredient(f: keyof RecipeIngredient, index: number) {
state.selectedRecipeField = `recipeIngredient.${index}.${f}` as SelectedRecipeLeaves;
}
// Leaves<RecipeStep[]> will return some function types making eslint very unhappy
type RecipeStepsLeaves = `${number}.${Leaves<RecipeStep>}`;
function setSingleStep(path: RecipeStepsLeaves) {
state.selectedRecipeField = `recipeInstructions.${path}` as SelectedRecipeLeaves;
}
return {
...toRefs(state),
addIngredient,
addStep,
drag,
assetURL,
updateRecipe,
closeEditor,
updateSelectedText,
tsv,
validators,
setSingleIngredient,
setSingleStep,
canvasSetText,
};
},
});
</script>
<style lang="css">
.ghost {
opacity: 0.5;
}
</style>

View File

@@ -1,488 +0,0 @@
<template>
<v-card flat tile>
<v-toolbar v-for="(section, idx) in toolbarIcons" :key="section.sectionTitle" dense style="float: left">
<v-toolbar-title bottom>
{{ section.sectionTitle }}
</v-toolbar-title>
<v-tooltip v-for="icon in section.icons" :key="icon.name" bottom>
<template #activator="{ on, attrs }">
<v-btn icon @click="section.eventHandler(icon.name)">
<v-icon :color="section.highlight === icon.name ? 'primary' : 'default'" v-bind="attrs" v-on="on">
{{ icon.icon }}
</v-icon>
</v-btn>
</template>
<span>{{ icon.tooltip }}</span>
</v-tooltip>
<v-divider v-if="idx != toolbarIcons.length - 1" vertical class="mx-2" />
</v-toolbar>
<v-toolbar dense style="float: right">
<BaseButton class="ml-1 mr-1" save @click="updateRecipe()">
{{ $t("general.save") }}
</BaseButton>
<BaseButton cancel @click="closeEditor()">
{{ $t("general.close") }}
</BaseButton>
</v-toolbar>
<canvas
ref="canvas"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@mousemove="handleMouseMove"
@wheel="handleMouseScroll"
>
</canvas>
<span style="white-space: pre-wrap">
{{ selectedText.trim() }}
</span>
</v-card>
</template>
<script lang="ts">
import { defineComponent, reactive, useContext, ref, toRefs, watch, onMounted } from "@nuxtjs/composition-api";
import { NoUndefinedField } from "~/lib/api/types/non-generated";
import { OcrTsvResponse as NullableOcrTsvResponse } from "~/lib/api/types/ocr";
import { CanvasModes, SelectedTextSplitModes, ImagePosition, Mouse, CanvasRect, ToolbarIcons } from "~/types/ocr-types";
// Temporary Shim until we have a better solution
// https://github.com/phillipdupuis/pydantic-to-typescript/issues/28
type OcrTsvResponse = NoUndefinedField<NullableOcrTsvResponse>;
export default defineComponent({
props: {
image: {
type: HTMLImageElement,
required: true,
},
tsv: {
type: Array as () => OcrTsvResponse[],
required: true,
},
},
setup(props, context) {
const state = reactive({
canvas: null as HTMLCanvasElement | null,
ctx: null as CanvasRenderingContext2D | null,
canvasRect: null as DOMRect | null,
rect: {
startX: 0,
startY: 0,
w: 0,
h: 0,
},
mouse: {
current: {
x: 0,
y: 0,
},
down: false,
},
selectedText: "",
canvasMode: "selection" as CanvasModes,
imagePosition: {
sx: 0,
sy: 0,
sWidth: 0,
sHeight: 0,
dx: 0,
dy: 0,
dWidth: 0,
dHeight: 0,
scale: 1,
panStartPoint: {
x: 0,
y: 0,
},
} as ImagePosition,
isImageSmallerThanCanvas: false,
selectedTextSplitMode: "lineNum" as SelectedTextSplitModes,
});
watch(
() => state.selectedText,
(value) => {
context.emit("text-selected", value);
}
);
onMounted(() => {
if (state.canvas === null) return; // never happens because the ref "canvas" is in the template
state.ctx = state.canvas.getContext("2d") as CanvasRenderingContext2D;
state.ctx.imageSmoothingEnabled = false;
state.canvasRect = state.canvas.getBoundingClientRect();
state.canvas.width = state.canvasRect.width;
if (props.image.width < state.canvas.width) {
state.isImageSmallerThanCanvas = true;
}
state.imagePosition.dWidth = state.canvas.width;
updateImageScale();
state.canvas.height = Math.min(props.image.height * state.imagePosition.scale, 700); // Max height of 700px
state.imagePosition.sWidth = props.image.width;
state.imagePosition.sHeight = props.image.height;
state.imagePosition.dWidth = state.canvas.width;
drawImage(state.ctx);
drawWordBoxesOnCanvas(props.tsv);
});
function handleMouseDown(event: MouseEvent) {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
state.mouse.down = true;
updateMousePos(event);
if (state.canvasMode === "selection") {
if (isMouseInRect(state.mouse, state.rect)) {
context.emit("setText", state.selectedText);
} else {
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
state.rect.startX = state.mouse.current.x;
state.rect.startY = state.mouse.current.y;
resetSelection();
}
return;
}
if (state.canvasMode === "panAndZoom") {
state.imagePosition.panStartPoint.x = state.mouse.current.x - state.imagePosition.dx;
state.imagePosition.panStartPoint.y = state.mouse.current.y - state.imagePosition.dy;
resetSelection();
}
}
function handleMouseUp(_event: MouseEvent) {
if (state.canvasRect === null) return;
state.mouse.down = false;
state.selectedText = getWordsInSelection(props.tsv, state.rect);
}
function handleMouseMove(event: MouseEvent) {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
updateMousePos(event);
if (state.mouse.down) {
if (state.canvasMode === "selection") {
state.rect.w = state.mouse.current.x - state.rect.startX;
state.rect.h = state.mouse.current.y - state.rect.startY;
draw();
return;
}
if (state.canvasMode === "panAndZoom") {
state.canvas.style.cursor = "move";
state.imagePosition.dx = state.mouse.current.x - state.imagePosition.panStartPoint.x;
state.imagePosition.dy = state.mouse.current.y - state.imagePosition.panStartPoint.y;
keepImageInCanvas();
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
return;
}
}
if (isMouseInRect(state.mouse, state.rect) && state.canvasMode === "selection") {
state.canvas.style.cursor = "pointer";
} else {
state.canvas.style.cursor = "default";
}
}
const scrollSensitivity = 0.05;
function handleMouseScroll(event: WheelEvent) {
if (state.isImageSmallerThanCanvas) return;
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
if (state.canvasMode === "panAndZoom") {
event.preventDefault();
updateMousePos(event);
const m = Math.sign(event.deltaY);
const ndx = state.imagePosition.dx + m * state.imagePosition.dWidth * scrollSensitivity;
const ndy = state.imagePosition.dy + m * state.imagePosition.dHeight * scrollSensitivity;
const ndw = state.imagePosition.dWidth + -m * state.imagePosition.dWidth * scrollSensitivity * 2;
const ndh = state.imagePosition.dHeight + -m * state.imagePosition.dHeight * scrollSensitivity * 2;
if (ndw < props.image.width) {
state.imagePosition.dx = ndx;
state.imagePosition.dy = ndy;
state.imagePosition.dWidth = ndw;
state.imagePosition.dHeight = ndh;
}
keepImageInCanvas();
updateImageScale();
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
}
}
function draw() {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
if (state.mouse.down) {
state.ctx.imageSmoothingEnabled = false;
state.ctx.fillStyle = "rgb(255, 255, 255)";
state.ctx.fillRect(0, 0, state.canvas.width, state.canvas.height);
drawImage(state.ctx);
state.ctx.fillStyle = "rgba(255, 255, 255, 0.1)";
state.ctx.setLineDash([6]);
state.ctx.fillRect(state.rect.startX, state.rect.startY, state.rect.w, state.rect.h);
state.ctx.strokeRect(state.rect.startX, state.rect.startY, state.rect.w, state.rect.h);
}
}
function drawImage(ctx: CanvasRenderingContext2D) {
ctx.drawImage(
props.image,
state.imagePosition.sx,
state.imagePosition.sy,
state.imagePosition.sWidth,
state.imagePosition.sHeight,
state.imagePosition.dx,
state.imagePosition.dy,
state.imagePosition.dWidth,
state.imagePosition.dHeight
);
}
function keepImageInCanvas() {
if (state.canvasRect === null || state.canvas === null) return;
// Prevent image from being smaller than the canvas width
if (state.imagePosition.dWidth - state.canvas.width < 0) {
state.imagePosition.dWidth = state.canvas.width;
}
// Prevent image from being smaller than the canvas height
if (state.imagePosition.dHeight - state.canvas.height < 0) {
state.imagePosition.dHeight = props.image.height * state.imagePosition.scale;
}
// Prevent to move the image too much to the left
if (state.canvas.width - state.imagePosition.dx - state.imagePosition.dWidth > 0) {
state.imagePosition.dx = state.canvas.width - state.imagePosition.dWidth;
}
// Prevent to move the image too much to the top
if (state.canvas.height - state.imagePosition.dy - state.imagePosition.dHeight > 0) {
state.imagePosition.dy = state.canvas.height - state.imagePosition.dHeight;
}
// Prevent to move the image too much to the right
if (state.imagePosition.dx > 0) {
state.imagePosition.dx = 0;
}
// Prevent to move the image too much to the bottom
if (state.imagePosition.dy > 0) {
state.imagePosition.dy = 0;
}
}
function updateImageScale() {
state.imagePosition.scale = state.imagePosition.dWidth / props.image.width;
// force the original ratio to be respected
state.imagePosition.dHeight = props.image.height * state.imagePosition.scale;
// Don't let images bigger than the canvas be zoomed in more than 1:1 scale
// Meaning only let images smaller than the canvas to have a scale > 1
if (!state.isImageSmallerThanCanvas && state.imagePosition.scale > 1) {
state.imagePosition.scale = 1;
}
}
function resetSelection() {
if (state.canvasRect === null) return;
state.rect.w = 0;
state.rect.h = 0;
state.selectedText = "";
}
function updateMousePos<T extends MouseEvent>(event: T) {
if (state.canvas === null) return;
state.canvasRect = state.canvas.getBoundingClientRect();
state.mouse.current = {
x: event.clientX - state.canvasRect.left,
y: event.clientY - state.canvasRect.top,
};
}
function isMouseInRect(mouse: Mouse, rect: CanvasRect) {
if (state.canvasRect === null) return;
const correctRect = correctRectCoordinates(rect);
return (
mouse.current.x > correctRect.startX &&
mouse.current.x < correctRect.startX + correctRect.w &&
mouse.current.y > correctRect.startY &&
mouse.current.y < correctRect.startY + correctRect.h
);
}
/**
* Returns rectangle coordinates with positive dimensions
* @param rect A rectangle
* @returns An equivalent rectangle with width and height > 0
*/
function correctRectCoordinates(rect: CanvasRect) {
if (rect.w < 0) {
rect.startX = rect.startX + rect.w;
rect.w = -rect.w;
}
if (rect.h < 0) {
rect.startY = rect.startY + rect.h;
rect.h = -rect.h;
}
return rect;
}
function drawWordBoxesOnCanvas(tsv: OcrTsvResponse[]) {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
state.ctx.fillStyle = "rgb(255, 255, 255, 0.3)";
tsv
.filter((element) => element.level === 5)
.forEach((element) => {
if (state.canvasRect === null || state.canvas === null || state.ctx === null) return;
state.ctx.fillRect(
element.left * state.imagePosition.scale,
element.top * state.imagePosition.scale,
element.width * state.imagePosition.scale,
element.height * state.imagePosition.scale
);
});
}
// Event emitters
const updateRecipe = function () {
context.emit("update-recipe");
};
const closeEditor = function () {
context.emit("close-editor");
};
// TOOLBAR STUFF
const { $globals, i18n } = useContext();
const toolbarIcons = ref<ToolbarIcons<CanvasModes | SelectedTextSplitModes>>([
{
sectionTitle: i18n.tc("ocr-editor.toolbar"),
eventHandler: switchCanvasMode,
highlight: state.canvasMode,
icons: [
{
name: "selection",
icon: $globals.icons.selectMode,
tooltip: i18n.tc("ocr-editor.selection-mode"),
},
{
name: "panAndZoom",
icon: $globals.icons.panAndZoom,
tooltip: i18n.tc("ocr-editor.pan-and-zoom-picture"),
},
],
},
{
sectionTitle: i18n.tc("ocr-editor.split-text"),
eventHandler: switchSplitTextMode,
highlight: state.selectedTextSplitMode,
icons: [
{
name: "lineNum",
icon: $globals.icons.preserveLines,
tooltip: i18n.tc("ocr-editor.preserve-line-breaks"),
},
{
name: "blockNum",
icon: $globals.icons.preserveBlocks,
tooltip: i18n.tc("ocr-editor.split-by-block"),
},
{
name: "flatten",
icon: $globals.icons.flatten,
tooltip: i18n.tc("ocr-editor.flatten"),
},
],
},
]);
function switchCanvasMode(mode: CanvasModes) {
if (state.canvasRect === null || state.canvas === null) return;
state.canvasMode = mode;
toolbarIcons.value[0].highlight = mode;
if (mode === "panAndZoom") {
state.canvas.style.cursor = "pointer";
} else {
state.canvas.style.cursor = "default";
}
}
function switchSplitTextMode(mode: SelectedTextSplitModes) {
if (state.canvasRect === null) return;
state.selectedTextSplitMode = mode;
toolbarIcons.value[1].highlight = mode;
state.selectedText = getWordsInSelection(props.tsv, state.rect);
}
/**
* Using rectangle coordinates, filters the tsv to get text elements contained
* inside the rectangle
* Additionaly adds newlines depending on the current "text split" mode
* @param tsv An Object containing tesseracts tsv fields
* @param rect Coordinates of a rectangle
* @returns Text from tsv contained in the rectangle
*/
function getWordsInSelection(tsv: OcrTsvResponse[], rect: CanvasRect) {
const correctedRect = correctRectCoordinates(rect);
return tsv
.filter(
(element) =>
element.level === 5 &&
correctedRect.startY - state.imagePosition.dy < element.top * state.imagePosition.scale &&
correctedRect.startX - state.imagePosition.dx < element.left * state.imagePosition.scale &&
correctedRect.startX + correctedRect.w >
(element.left + element.width) * state.imagePosition.scale + state.imagePosition.dx &&
correctedRect.startY + correctedRect.h >
(element.top + element.height) * state.imagePosition.scale + state.imagePosition.dy
)
.map((element, index, array) => {
let separator = " ";
if (
state.selectedTextSplitMode !== "flatten" &&
index !== array.length - 1 &&
element[state.selectedTextSplitMode] !== array[index + 1][state.selectedTextSplitMode]
) {
separator = "\n";
}
return element.text + separator;
})
.join("");
}
return {
...toRefs(state),
handleMouseDown,
handleMouseUp,
handleMouseMove,
handleMouseScroll,
toolbarIcons,
updateRecipe,
closeEditor,
};
},
});
</script>

View File

@@ -1,54 +0,0 @@
<template>
<v-card>
<v-app-bar dense dark color="primary" class="mb-2">
<v-icon large left>
{{ $globals.icons.help }}
</v-icon>
<v-toolbar-title class="headline"> {{ $t("ocr-editor.help.help") }} </v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<v-card-text>
<h1> {{ $t("ocr-editor.help.mouse-modes") }}</h1>
<v-divider class="mb-2 mt-1" />
<h2 class="my-2">
<v-icon> {{ $globals.icons.selectMode }} </v-icon>{{ $t("ocr-editor.help.selection-mode") }}
</h2>
<p class="my-1">{{ $t("ocr-editor.help.selection-mode") }}</p>
<ol>
<li>{{ $t("ocr-editor.help.selection-mode-steps.draw") }}</li>
<li>{{ $t("ocr-editor.help.selection-mode-steps.click") }}</li>
<li>{{ $t("ocr-editor.help.selection-mode-steps.result") }}</li>
</ol>
<h2 class="my-2">
<v-icon> {{ $globals.icons.panAndZoom }} </v-icon>{{ $t("ocr-editor.help.pan-and-zoom-mode") }}
</h2>
{{ $t("ocr-editor.help.pan-and-zoom-desc") }}
<h1 class="mt-5">{{ $t("ocr-editor.help.split-text-mode") }}</h1>
<v-divider class="mb-2 mt-1" />
<h2 class="my-2">
<v-icon> {{ $globals.icons.preserveLines }} </v-icon>
{{ $t("ocr-editor.help.split-modes.line-mode") }}
</h2>
<p>
{{ $t("ocr-editor.help.split-modes.line-mode-desc") }}
</p>
<h2 class="my-2">
<v-icon> {{ $globals.icons.preserveBlocks }} </v-icon>
{{ $t("ocr-editor.help.split-modes.block-mode") }}
</h2>
<p>
{{ $t("ocr-editor.help.split-modes.block-mode-desc") }}
</p>
<h2 class="my-2">
<v-icon> {{ $globals.icons.flatten }} </v-icon> {{ $t("ocr-editor.help.split-modes.flat-mode") }}
</h2>
<p>{{ $t("ocr-editor.help.split-modes.flat-mode-desc") }}</p>
</v-card-text>
</v-card>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
export default defineComponent({});
</script>

View File

@@ -1,3 +0,0 @@
import RecipeOcrEditorPage from "./RecipeOcrEditorPage.vue";
export default RecipeOcrEditorPage;

View File

@@ -5,12 +5,15 @@
<BaseDialog
v-if="deleteTarget"
v-model="dialogs.delete"
:title="$t('general.delete-with-name', { name: deleteTarget.name })"
:title="$t('general.delete-with-name', { name: $t(translationKey) })"
color="error"
:icon="$globals.icons.alertCircle"
@confirm="deleteOne()"
>
<v-card-text> {{ $t("general.confirm-delete-generic-with-name", { name: deleteTarget.name }) }} </v-card-text>
<v-card-text>
<p>{{ $t("general.confirm-delete-generic-with-name", { name: $t(translationKey) }) }}</p>
<p class="mt-4 mb-0 ml-4">{{ deleteTarget.name }}</p>
</v-card-text>
</BaseDialog>
<BaseDialog v-if="updateTarget" v-model="dialogs.update" :title="$t('general.update')" @confirm="updateOne()">
@@ -136,6 +139,15 @@ export default defineComponent({
const presets = useContextPresets();
const translationKey = computed<string>(() => {
const typeMap = {
"categories": "category.category",
"tags": "tag.tag",
"tools": "tool.tool"
};
return typeMap[props.itemType] || "";
});
const deleteTarget = ref<GenericItem | null>(null);
const updateTarget = ref<GenericItem | null>(null);
@@ -223,6 +235,7 @@ export default defineComponent({
presets,
itemsSorted,
searchString,
translationKey,
};
},
// Needed for useMeta

View File

@@ -330,9 +330,6 @@ export default defineComponent({
.list-group {
min-height: 38px;
}
.list-group-item {
cursor: move;
}
.list-group-item i {
cursor: pointer;
}

View File

@@ -50,7 +50,6 @@
:logged-in="isOwnGroup"
:open="isEditMode"
:recipe-id="recipe.id"
:show-ocr-button="recipe.isOcrRecipe"
class="ml-auto mt-n8 pb-4"
@close="setMode(PageMode.VIEW)"
@json="toggleEditMode()"
@@ -58,13 +57,12 @@
@save="$emit('save')"
@delete="$emit('delete')"
@print="printRecipe"
@ocr="goToOcrEditor"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, useContext, computed, ref, watch, useRouter, useRoute } from "@nuxtjs/composition-api";
import { defineComponent, useContext, computed, ref, watch } from "@nuxtjs/composition-api";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
import RecipeLastMade from "~/components/Domain/Recipe/RecipeLastMade.vue";
@@ -96,16 +94,12 @@ export default defineComponent({
},
},
setup(props) {
const { $auth, $vuetify } = useContext();
const { $vuetify } = useContext();
const { recipeImage } = useStaticRoutes();
const { imageKey, pageMode, editMode, setMode, toggleEditMode, isEditMode } = usePageState(props.recipe.slug);
const { user } = usePageUser();
const { isOwnGroup } = useLoggedInState();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
function printRecipe() {
window.print();
}
@@ -119,10 +113,6 @@ export default defineComponent({
return recipeImage(props.recipe.id, props.recipe.image, imageKey.value);
});
function goToOcrEditor() {
router.push(`/g/${groupSlug.value}/r/${props.recipe.slug}/ocr-editor`);
}
watch(
() => recipeImageUrl.value,
() => {
@@ -146,7 +136,6 @@ export default defineComponent({
hideImage,
isEditMode,
recipeImageUrl,
goToOcrEditor,
};
},
});

View File

@@ -1,7 +1,7 @@
<template>
<section @keyup.ctrl.90="undoMerge">
<!-- Ingredient Link Editor -->
<v-dialog v-model="dialog" width="600">
<v-dialog v-if="dialog" v-model="dialog" width="600">
<v-card :ripple="false">
<v-app-bar dark color="primary" class="mt-n1 mb-3">
<v-icon large left>
@@ -50,11 +50,15 @@
<BaseButton cancel @click="dialog = false"> </BaseButton>
<v-spacer></v-spacer>
<div class="d-flex flex-wrap justify-end">
<BaseButton color="info" @click="autoSetReferences">
<BaseButton class="my-1" color="info" @click="autoSetReferences">
<template #icon> {{ $globals.icons.robot }}</template>
{{ $t("recipe.auto") }}
</BaseButton>
<BaseButton class="ml-2" save @click="setIngredientIds"> </BaseButton>
<BaseButton class="ml-2 my-1" save @click="setIngredientIds"> </BaseButton>
<BaseButton v-if="availableNextStep" class="ml-2 my-1" @click="saveAndOpenNextLinkIngredients">
<template #icon> {{ $globals.icons.forward }}</template>
{{ $t("recipe.nextStep") }}
</BaseButton>
</div>
</v-card-actions>
</v-card>
@@ -117,7 +121,7 @@
@click="toggleDisabled(index)"
>
<v-card-title :class="{ 'pb-0': !isChecked(index) }">
<span class="handle">
<span :class="isEditForm ? 'handle' : ''">
<v-icon v-if="isEditForm" size="26" class="pb-1">{{ $globals.icons.arrowUpDown }}</v-icon>
{{ $t("recipe.step-index", { step: index + 1 }) }}
</span>
@@ -236,6 +240,7 @@ import {
onMounted,
useContext,
computed,
nextTick,
} from "@nuxtjs/composition-api";
import RecipeIngredientHtml from "../../RecipeIngredientHtml.vue";
import { RecipeStep, IngredientReferences, RecipeIngredient, RecipeAsset, Recipe } from "~/lib/api/types/recipe";
@@ -399,6 +404,8 @@ export default defineComponent({
activeRefs.value = refs.map((ref) => ref.referenceId ?? "");
}
const availableNextStep = computed(() => activeIndex.value < props.value.length - 1);
function setIngredientIds() {
const instruction = props.value[activeIndex.value];
instruction.ingredientReferences = activeRefs.value.map((ref) => {
@@ -417,6 +424,20 @@ export default defineComponent({
state.dialog = false;
}
function saveAndOpenNextLinkIngredients() {
const currentStepIndex = activeIndex.value;
if(!availableNextStep.value) {
return; // no next step, the button calling this function should not be shown
}
setIngredientIds();
const nextStep = props.value[currentStepIndex + 1];
// close dialog before opening to reset the scroll position
nextTick(() => openDialog(currentStepIndex + 1, nextStep.text, nextStep.ingredientReferences));
}
function setUsedIngredients() {
const usedRefs: { [key: string]: boolean } = {};
@@ -627,6 +648,8 @@ export default defineComponent({
mergeAbove,
openDialog,
setIngredientIds,
availableNextStep,
saveAndOpenNextLinkIngredients,
undoMerge,
toggleDisabled,
isChecked,
@@ -671,9 +694,6 @@ export default defineComponent({
.list-group {
min-height: 38px;
}
.list-group-item {
cursor: move;
}
.list-group-item i {
cursor: pointer;
}

View File

@@ -2,7 +2,7 @@
<v-tooltip
ref="copyToolTip"
v-model="show"
color="success lighten-1"
:color="copied? 'success lighten-1' : 'red lighten-1'"
top
:open-on-hover="false"
:open-on-click="true"
@@ -29,12 +29,14 @@
<v-icon left dark>
{{ $globals.icons.clipboardCheck }}
</v-icon>
<slot> {{ $t("general.copied_message") }} </slot>
<slot v-if="!isSupported"> {{ $t("general.your-browser-does-not-support-clipboard") }} </slot>
<slot v-else> {{ copied ? $t("general.copied_message") : $t("general.clipboard-copy-failure") }} </slot>
</span>
</v-tooltip>
</template>
<script lang="ts">
import { useClipboard } from "@vueuse/core"
import { defineComponent, ref } from "@nuxtjs/composition-api";
import { VTooltip } from "~/types/vuetify";
@@ -58,6 +60,7 @@ export default defineComponent({
},
},
setup(props) {
const { copy, copied, isSupported } = useClipboard()
const show = ref(false);
const copyToolTip = ref<VTooltip | null>(null);
@@ -65,13 +68,21 @@ export default defineComponent({
copyToolTip.value?.deactivate();
}
function textToClipboard() {
async function textToClipboard() {
if (isSupported.value) {
await copy(props.copyText);
if (copied.value) {
console.log(`Copied\n${props.copyText}`)
}
else {
console.warn("Copy failed: ", copied.value);
}
}
else {
console.warn("Clipboard is currently not supported by your browser. Ensure you're on a secure (https) site.");
}
show.value = true;
const copyText = props.copyText;
navigator.clipboard.writeText(copyText).then(
() => console.log(`Copied\n${copyText}`),
() => console.log(`Copied Failed\n${copyText}`)
);
setTimeout(() => {
toggleBlur();
}, 500);
@@ -81,6 +92,8 @@ export default defineComponent({
show,
copyToolTip,
textToClipboard,
copied,
isSupported,
};
},
});

View File

@@ -49,6 +49,10 @@ export default defineComponent({
];
function handleRowClick(item: ReportSummary) {
if (item.status === "in-progress") {
return;
}
router.push(`/group/reports/${item.id}`);
}

View File

@@ -7,23 +7,30 @@ export function useCopy() {
const { i18n } = useContext();
function copyText(text: string) {
if (!isSupported) {
if (!isSupported.value) {
alert.error(i18n.tc("general.clipboard-not-supported"));
return;
}
copy(text);
alert.success(i18n.tc("general.copied-to-clipboard"));
copy(text).then(() => {
// Verify copy success as no error is thrown on failure.
if (copied.value) {
alert.success(i18n.tc("general.copied-to-clipboard"));
}
else {
alert.error(i18n.tc("general.clipboard-copy-failure"));
}
});
}
return { copyText, copied };
}
export function useCopyList() {
const { copy, isSupported } = useClipboard();
const { copy, isSupported, copied } = useClipboard();
const { i18n } = useContext();
function checkClipboard() {
if (!isSupported) {
if (!isSupported.value) {
alert.error(i18n.tc("general.your-browser-does-not-support-clipboard"));
return false;
}
@@ -54,7 +61,13 @@ export function useCopyList() {
function copyText(text: string, len: number) {
copy(text).then(() => {
alert.success(i18n.tc("general.copied-items-to-clipboard", len));
// Verify copy success as no error is thrown on failure.
if (copied.value) {
alert.success(i18n.tc("general.copied-items-to-clipboard", len));
}
else {
alert.error(i18n.tc("general.clipboard-copy-failure"));
}
});
}

View File

@@ -3,196 +3,241 @@ export const LOCALES = [
{
name: "繁體中文 (Chinese traditional)",
value: "zh-TW",
progress: 28,
progress: 30,
dir: "ltr",
},
{
name: "简体中文 (Chinese simplified)",
value: "zh-CN",
progress: 65,
progress: 98,
dir: "ltr",
},
{
name: "Tiếng Việt (Vietnamese)",
value: "vi-VN",
progress: 2,
progress: 1,
dir: "ltr",
},
{
name: "Українська (Ukrainian)",
value: "uk-UA",
progress: 99,
progress: 100,
dir: "ltr",
},
{
name: "Türkçe (Turkish)",
value: "tr-TR",
progress: 50,
progress: 53,
dir: "ltr",
},
{
name: "Svenska (Swedish)",
value: "sv-SE",
progress: 71,
progress: 94,
dir: "ltr",
},
{
name: "српски (Serbian)",
value: "sr-SP",
progress: 4,
progress: 32,
dir: "ltr",
},
{
name: "Slovenian",
value: "sl-SI",
progress: 49,
progress: 47,
dir: "ltr",
},
{
name: "Slovak",
value: "sk-SK",
progress: 97,
progress: 93,
dir: "ltr",
},
{
name: "Pусский (Russian)",
value: "ru-RU",
progress: 99,
progress: 98,
dir: "ltr",
},
{
name: "Română (Romanian)",
value: "ro-RO",
progress: 32,
progress: 42,
dir: "ltr",
},
{
name: "Português (Portuguese)",
value: "pt-PT",
progress: 99,
progress: 100,
dir: "ltr",
},
{
name: "Português do Brasil (Brazilian Portuguese)",
value: "pt-BR",
progress: 98,
progress: 97,
dir: "ltr",
},
{
name: "Polski (Polish)",
value: "pl-PL",
progress: 97,
progress: 98,
dir: "ltr",
},
{
name: "Norsk (Norwegian)",
value: "no-NO",
progress: 85,
progress: 99,
dir: "ltr",
},
{
name: "Nederlands (Dutch)",
value: "nl-NL",
progress: 98,
progress: 100,
dir: "ltr",
},
{
name: "Latvian",
value: "lv-LV",
progress: 1,
dir: "ltr",
},
{
name: "Lithuanian",
value: "lt-LT",
progress: 97,
progress: 93,
dir: "ltr",
},
{
name: "한국어 (Korean)",
value: "ko-KR",
progress: 5,
dir: "ltr",
},
{
name: "日本語 (Japanese)",
value: "ja-JP",
progress: 11,
progress: 12,
dir: "ltr",
},
{
name: "Italiano (Italian)",
value: "it-IT",
progress: 96,
progress: 100,
dir: "ltr",
},
{
name: "Icelandic",
value: "is-IS",
progress: 0,
dir: "ltr",
},
{
name: "Magyar (Hungarian)",
value: "hu-HU",
progress: 99,
progress: 100,
dir: "ltr",
},
{
name: "Croatian",
value: "hr-HR",
progress: 97,
progress: 93,
dir: "ltr",
},
{
name: "עברית (Hebrew)",
value: "he-IL",
progress: 99,
progress: 97,
dir: "rtl",
},
{
name: "Galician",
value: "gl-ES",
progress: 1,
dir: "ltr",
},
{
name: "Français (French)",
value: "fr-FR",
progress: 99,
progress: 100,
dir: "ltr",
},
{
name: "French, Canada",
value: "fr-CA",
progress: 97,
dir: "ltr",
},
{
name: "Suomi (Finnish)",
value: "fi-FI",
progress: 95,
progress: 91,
dir: "ltr",
},
{
name: "Español (Spanish)",
value: "es-ES",
progress: 76,
progress: 79,
dir: "ltr",
},
{
name: "American English",
value: "en-US",
progress: 100.0,
dir: "ltr",
},
{
name: "British English",
value: "en-GB",
progress: 4,
progress: 3,
dir: "ltr",
},
{
name: "Ελληνικά (Greek)",
value: "el-GR",
progress: 35,
progress: 34,
dir: "ltr",
},
{
name: "Deutsch (German)",
value: "de-DE",
progress: 99,
progress: 100,
dir: "ltr",
},
{
name: "Dansk (Danish)",
value: "da-DK",
progress: 100,
progress: 98,
dir: "ltr",
},
{
name: "Čeština (Czech)",
value: "cs-CZ",
progress: 66,
progress: 64,
dir: "ltr",
},
{
name: "Català (Catalan)",
value: "ca-ES",
progress: 61,
progress: 75,
dir: "ltr",
},
{
name: "Bulgarian",
value: "bg-BG",
progress: 99,
dir: "ltr",
},
{
name: "العربية (Arabic)",
value: "ar-SA",
progress: 16,
progress: 20,
dir: "rtl",
},
{
name: "Afrikaans (Afrikaans)",
value: "af-ZA",
progress: 96,
progress: 92,
dir: "ltr",
},
]

View File

@@ -4,14 +4,31 @@ import { LOCALES } from "./available-locales";
export const useLocales = () => {
const { i18n, $vuetify } = useContext();
function getLocale(value: string) {
const currentLocale = LOCALES.filter((locale) => locale.value === value);
return currentLocale.length ? currentLocale[0] : null;
}
const locale = computed<string>({
get() {
$vuetify.lang.current = i18n.locale; // dirty hack
// dirty hack
$vuetify.lang.current = i18n.locale;
const currentLocale = getLocale(i18n.locale);
if (currentLocale) {
$vuetify.rtl = currentLocale.dir === "rtl";
}
return i18n.locale;
},
set(value) {
i18n.setLocale(value);
$vuetify.lang.current = value; // this does not persist after window reload :-(
// this does not persist after window reload :-(
$vuetify.lang.current = value;
const currentLocale = getLocale(value);
if (currentLocale) {
$vuetify.rtl = currentLocale.dir === "rtl";
}
// Reload the page to update the language - not all strings are reactive
window.location.reload();

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Sleutelwoord",
"link-copied": "Skakel gekopieer",
"loading": "Loading",
"loading-events": "Besig om gebeurtenisse te laai",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Verfris",
"upload-file": "Laai dokument op",
"created-on-date": "Geskep op: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Is jy seker jy wil <b>{groupName}<b/> uitvee?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Skep 'n nuwe maaltydplan",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Aandete hierdie week",
"dinner-today": "Vandag se Aandete",
"dinner-tonight": "VANAAND SE AANDETE",
@@ -470,9 +473,11 @@
"add-to-timeline": "Voeg by tydlyn",
"recipe-added-to-list": "Resep by lys gevoeg",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Resep is by die maaltydplan gevoeg",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Kon nie resep by maaltydplan voeg nie",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Resultaat",
"quantity": "Hoeveelheid",
"choose-unit": "Kies 'n eenheid",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Hoe het dit uitgedraai?",
"user-made-this": "{user} het dit gemaak",
"last-made-date": "Laas gemaak {date}",
"api-extras-description": "Resepte ekstras is 'n belangrike funksie van die Mealie API. Hulle laat jou toe om pasgemaakte json-sleutel/waarde-pare binne 'n resep te skep om na derdeparty-toepassings te verwys. Jy kan hierdie sleutels gebruik om inligting te bevat om outomatisering of pasgemaakte boodskappe na die gewenste toestel te stuur.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Boodskap sleutel",
"parse": "Verwerk",
"attach-images-hint": "Voeg prente by deur dit in die bewerker te sleep en los",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Nuwe resepname moet uniek wees",
"scrape-recipe": "Skraap resep",
"scrape-recipe-description": "Voeg 'n resep by via 'n url. Voer die url van die webwerf in wat jy vir 'n resep wil skandeer, Mealie sal probeer om die resep vanaf daardie plek te skandeer en by jou versameling te voeg.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Voer oorspronklike sleutelwoorde as merkers in",
"stay-in-edit-mode": "Bly in redigeer modus",
"import-from-zip": "Voer vanaf zip in",
@@ -553,7 +560,8 @@
"unit": "Eenheid",
"upload-image": "Laai prent",
"screen-awake": "Hou die skerm aan",
"remove-image": "Verwyder prent"
"remove-image": "Verwyder prent",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Gevorderde soek",
@@ -787,7 +795,8 @@
"tags": "Merkers",
"untagged-count": "Nie gemerk {count}",
"create-a-tag": "Skep 'n merker",
"tag-name": "Merker naam"
"tag-name": "Merker naam",
"tag": "Tag"
},
"tool": {
"tools": "Kookgerei",
@@ -796,7 +805,8 @@
"tool-name": "Naam van die kookgerei",
"create-new-tool": "Skep nuwe kookgerei",
"on-hand-checkbox-label": "Wys as in besit (gemerk)",
"required-tools": "Vereiste kookgerei"
"required-tools": "Vereiste kookgerei",
"tool": "Tool"
},
"user": {
"admin": "Administrateur",

View File

@@ -114,9 +114,10 @@
"json": "JSON",
"keyword": "كلمة مفتاحية",
"link-copied": "تمّ نسْخ الرّابط",
"loading": "جار التحميل",
"loading-events": "جاري تحميل الأحداث",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "جار تحميل الوصفات...",
"loading-ocr-data": "جاري تحميل بيانات OCR...",
"loading-recipes": "جار تحميل الوصفات",
"message": "الرسائل النصية Sms",
"monday": "الإثنين",
@@ -127,7 +128,7 @@
"no-recipe-found": "لم يتم العثور على وصفة",
"ok": "موافق",
"options": "الخيارات:",
"plural-name": "Plural Name",
"plural-name": "إسم المعدود",
"print": "طباعة",
"print-preferences": "إعدادات الطباعة",
"random": "عشوائي",
@@ -190,14 +191,15 @@
"clipboard-not-supported": "الحافظة غير مدعومة",
"copied-to-clipboard": "نُسِخَ إلى الحافظة",
"your-browser-does-not-support-clipboard": "المتصفح الخاص بك لا يدعم الحافظة",
"copied-items-to-clipboard": "No item copied to clipboard|One item copied to clipboard|Copied {count} items to clipboard",
"copied-items-to-clipboard": "لم يتم نسخ أي عنصر إلى الحافظة<unk> عنصر واحد تم نسخه إلى الحافظة<unk> تم نسخه إلى الحافظة {count}",
"actions": "الإجراءت",
"selected-count": "تم اختيار: {count}",
"export-all": "تصدير الكل",
"refresh": "تحديث",
"upload-file": "تحميل الملف",
"created-on-date": "تم الإنشاء في {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "هل انت متأكد من رغبتك في حذف <b>{groupName}<b/>؟",
@@ -212,7 +214,7 @@
"group-id-with-value": "رقم تعريف المجموعة: {groupID}",
"group-name": "اسم المجموعة",
"group-not-found": "لم يتم العثور على المجموعة",
"group-token": "Group Token",
"group-token": "رمز المجموعة",
"group-with-value": "المجموعة: {groupID}",
"groups": "المجموعات",
"manage-groups": "إدارة المجموعات",
@@ -225,29 +227,29 @@
},
"manage-members": "إدارة الأعضاء",
"manage-members-description": "Manage the permissions of the members in your groups. {manage} allows the user to access the data-management page {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
"manage": "Manage",
"invite": "Invite",
"looking-to-update-your-profile": "Looking to Update Your Profile?",
"default-recipe-preferences-description": "These are the default settings when a new recipe is created in your group. These can be changed for individual recipes in the recipe settings menu.",
"manage": "إدارة الحساب",
"invite": "دعوة",
"looking-to-update-your-profile": "هل ترغب في تحديث ملفك الشخصي؟",
"default-recipe-preferences-description": "هذه هي الإعدادات الافتراضية عند إنشاء وصفة جديدة في مجموعتك. يمكن تغيير هذه الوصفات الفردية في قائمة إعدادات الوصفات.",
"default-recipe-preferences": "Default Recipe Preferences",
"group-preferences": "Group Preferences",
"private-group": "Private Group",
"group-preferences": "إعدادات المجموعة",
"private-group": "مجموعة خاصة",
"private-group-description": "Setting your group to private will default all public view options to default. This overrides an individual recipes public view settings.",
"allow-users-outside-of-your-group-to-see-your-recipes": "Allow users outside of your group to see your recipes",
"allow-users-outside-of-your-group-to-see-your-recipes": "السماح للمستخدمين خارج مجموعتك لمشاهدة وصفاتك",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your group or with a pre-generated private link",
"show-nutrition-information": "Show nutrition information",
"show-nutrition-information": "عرض معلومات التغذية",
"show-nutrition-information-description": "When enabled the nutrition information will be shown on the recipe if available. If there is no nutrition information available, the nutrition information will not be shown",
"show-recipe-assets": "Show recipe assets",
"show-recipe-assets-description": "When enabled the recipe assets will be shown on the recipe if available",
"default-to-landscape-view": "Default to landscape view",
"default-to-landscape-view-description": "When enabled the recipe header section will be shown in landscape view",
"disable-users-from-commenting-on-recipes": "Disable users from commenting on recipes",
"disable-users-from-commenting-on-recipes": "إيقاف المستخدمين من التعليق على الوصفات",
"disable-users-from-commenting-on-recipes-description": "Hides the comment section on the recipe page and disables commenting",
"disable-organizing-recipe-ingredients-by-units-and-food": "Disable organizing recipe ingredients by units and food",
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields.",
"general-preferences": "General Preferences",
"group-recipe-preferences": "Group Recipe Preferences",
"report": "Report",
"report": "تقرير",
"report-with-id": "Report ID: {id}",
"group-management": "Group Management",
"admin-group-management": "Admin Group Management",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "إنشاء خطة وجبة جديدة",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "العشاء لهذا الأسبوع",
"dinner-today": "العشاء اليوم",
"dinner-tonight": "العشاء الليلة",
@@ -289,34 +292,34 @@
"type-any": "أي",
"day-any": "أي",
"editor": "Editor",
"meal-recipe": "Meal Recipe",
"meal-title": "Meal Title",
"meal-note": "Meal Note",
"note-only": "Note Only",
"random-meal": "Random Meal",
"random-dinner": "Random Dinner",
"meal-recipe": "وصفة الوجبة",
"meal-title": "عنوان الوجبة",
"meal-note": "ملاحظة الوجبة",
"note-only": "ملاحظة فقط",
"random-meal": "وجبة عشوائية",
"random-dinner": "عشاء عشوائي",
"random-side": "Random Side",
"this-rule-will-apply": "This rule will apply {dayCriteria} {mealTypeCriteria}.",
"to-all-days": "to all days",
"to-all-days": "إلى جميع الأيام",
"on-days": "on {0}s",
"for-all-meal-types": "for all meal types",
"for-all-meal-types": "لجميع أنواع الوجبات",
"for-type-meal-types": "for {0} meal types",
"meal-plan-rules": "Meal Plan Rules",
"new-rule": "New Rule",
"new-rule": "قاعدة جديدة",
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the categories of the rules will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
"new-rule-description": "When creating a new rule for a meal plan you can restrict the rule to be applicable for a specific day of the week and/or a specific type of meal. To apply a rule to all days or all meal types you can set the rule to \"Any\" which will apply it to all the possible values for the day and/or meal type.",
"recipe-rules": "قواعد الوصفات",
"applies-to-all-days": "Applies to all days",
"applies-to-all-days": "ينطبق على جميع الأيام",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
},
"migration": {
"migration-data-removed": "Migration data removed",
"new-migration": "New Migration",
"no-file-selected": "No File Selected",
"no-file-selected": "لم يتمّ اختيار أيّ ملفّ",
"no-migration-data-available": "No Migration Data Available",
"previous-migrations": "Previous Migrations",
"recipe-migration": "Recipe Migration",
"recipe-migration": "نقل الوصفة",
"chowdown": {
"description": "Migrate data from Chowdown",
"description-long": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.",
@@ -384,12 +387,12 @@
"404-page-not-found": "404 Page not found",
"all-recipes": "All Recipes",
"new-page-created": "New page created",
"page": "Page",
"page": "الصفحة",
"page-creation-failed": "Page creation failed",
"page-deleted": "Page deleted",
"page-deletion-failed": "Page deletion failed",
"page-update-failed": "Page update failed",
"page-updated": "Page updated",
"page-deleted": "تم حذف الصفحة",
"page-deletion-failed": "حذف الصفحة فشل",
"page-update-failed": "تحديث الصفحة فشل",
"page-updated": "تم تحديث صفحة",
"pages-update-failed": "Pages update failed",
"pages-updated": "Pages updated",
"404-not-found": "لم يتم العثور على الصفحة. خطأ 404",
@@ -470,9 +473,11 @@
"add-to-timeline": "Add to Timeline",
"recipe-added-to-list": "Recipe added to list",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recipe added to mealplan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Failed to add recipe to mealplan",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Yield",
"quantity": "Quantity",
"choose-unit": "Choose Unit",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "How did it turn out?",
"user-made-this": "{user} made this",
"last-made-date": "Last Made {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Import original keywords as tags",
"stay-in-edit-mode": "Stay in Edit mode",
"import-from-zip": "Import from Zip",
@@ -553,7 +560,8 @@
"unit": "Unit",
"upload-image": "Upload image",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Advanced Search",
@@ -787,7 +795,8 @@
"tags": "Tags",
"untagged-count": "Untagged {count}",
"create-a-tag": "Create a Tag",
"tag-name": "Tag Name"
"tag-name": "Tag Name",
"tag": "Tag"
},
"tool": {
"tools": "Tools",
@@ -796,7 +805,8 @@
"tool-name": "Tool Name",
"create-new-tool": "Create New Tool",
"on-hand-checkbox-label": "Show as On Hand (Checked)",
"required-tools": "Required Tools"
"required-tools": "Required Tools",
"tool": "Tool"
},
"user": {
"admin": "Admin",

View File

@@ -77,7 +77,7 @@
"tag-events": "Събития за таг",
"category-events": "Събития за категория",
"when-a-new-user-joins-your-group": "Когато потребител се присъедини към твоята потребителска група",
"recipe-events": "Recipe Events"
"recipe-events": "Събития на рецептата"
},
"general": {
"cancel": "Откажи",
@@ -114,9 +114,10 @@
"json": "JSON",
"keyword": "Ключова дума",
"link-copied": "Линкът е копиран",
"loading": "Зареждане",
"loading-events": "Зареждане на събития",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Зареждане на рецептата...",
"loading-ocr-data": "Зареждане на OCR данните...",
"loading-recipes": "Рецептите се зареждат",
"message": "Съобщение",
"monday": "Понеделник",
@@ -127,7 +128,7 @@
"no-recipe-found": "Няма намерени рецепти",
"ok": "Добре",
"options": "Опции:",
"plural-name": "Plural Name",
"plural-name": "Име в множествено число",
"print": "Принтирай",
"print-preferences": "Настройки на принтиране",
"random": "Произволно",
@@ -197,7 +198,8 @@
"refresh": "Опресни",
"upload-file": "Качване на файл",
"created-on-date": "Създадено на {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "Имате незапазени промени. Желаете ли да ги запазите преди да излезете? Натиснете Ок за запазване и Отказ за отхвърляне на промените.",
"clipboard-copy-failure": "Линкът към рецептата е копиран в клипборда."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Сигурни ли сте, че искате да изтриете <b>{groupName}<b/>?",
@@ -212,7 +214,7 @@
"group-id-with-value": "ID на Групата: {groupID}",
"group-name": "Име на групата",
"group-not-found": "Групата не е намерена",
"group-token": "Group Token",
"group-token": "Токен на групата",
"group-with-value": "Група: {groupID}",
"groups": "Групи",
"manage-groups": "Управление на групи",
@@ -248,7 +250,7 @@
"general-preferences": "Общи предпочитания",
"group-recipe-preferences": "Предпочитания за рецепта по група",
"report": "Сигнал",
"report-with-id": "Report ID: {id}",
"report-with-id": "Номер на доклада: {id}",
"group-management": "Управление на групите",
"admin-group-management": "Административно управление на групите",
"admin-group-management-text": "Промените по тази група ще бъдат отразени моментално.",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Създаване на нов хранителен план",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Вечеря тази седмица",
"dinner-today": "Вечеря Днес",
"dinner-tonight": "Вечеря ТАЗИ ВЕЧЕР",
@@ -469,10 +472,12 @@
"add-to-plan": "Добави към план",
"add-to-timeline": "Добави към времевата линия",
"recipe-added-to-list": "Рецептата е добавена към списъка",
"recipes-added-to-list": "Recipes added to list",
"recipes-added-to-list": "Рецептите са добавени към списъка",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Рецептата е добавена към хранителния план",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipes-to-list": "Неуспешно добавяне на рецепта към списъка",
"failed-to-add-recipe-to-mealplan": "Рецептата не беше добавена към хранителния план",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Добив",
"quantity": "Количество",
"choose-unit": "Избери единица",
@@ -509,11 +514,11 @@
"how-did-it-turn-out": "Как се получи?",
"user-made-this": "{user} направи това",
"last-made-date": "Последно приготвена на {date}",
"api-extras-description": "Екстрите за рецепти са ключова характеристика на Mealie API. Те Ви позволяват да създавате персонализирани json двойки ключ/стойност в рамките на рецепта, за да ги препращате към други приложения. Можете да използвате тези ключове, за да съдържате информация за задействане на автоматизация или персонализирани съобщения, за препращане към желаното от Вас устройство.",
"api-extras-description": "Екстрите за рецепти са ключова характеристика на Mealie API. Те Ви позволяват да създавате персонализирани JSON двойки ключ/стойност в рамките на рецепта, за да ги препращате към други приложения. Можете да използвате тези ключове, за да предоставите информация за задействане на автоматизация или персонализирани съобщения, за препращане към желаното от Вас устройство.",
"message-key": "Ключ на съобщението",
"parse": "Анализирай",
"attach-images-hint": "Прикачете снимки като ги влачете и пуснете в редактора",
"drop-image": "Drop image",
"drop-image": "Премахване на изображение",
"enable-ingredient-amounts-to-use-this-feature": "Пуснете количествата на съставките за да използвате функционалността",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Рецепти със зададени мерни единици и храни ме могат да бъдат анализирани.",
"parse-ingredients": "Анализирай съставките",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Името на рецептата трябва да бъде уникално",
"scrape-recipe": "Обхождане на рецепта",
"scrape-recipe-description": "Обходи рецепта по линк. Предоставете линк за сайт, който искате да бъде обходен. Mealie ще опита да обходи рецептата от този сайт и да я добави във Вашата колекция.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Импортирай оригиналните ключови думи като тагове",
"stay-in-edit-mode": "Остани в режим на редакция",
"import-from-zip": "Импортирай от Zip",
@@ -553,7 +560,8 @@
"unit": "Единица",
"upload-image": "Качване на изображение",
"screen-awake": "Запази екрана активен",
"remove-image": "Премахване на изображение"
"remove-image": "Премахване на изображение",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Разширено търсене",
@@ -572,16 +580,16 @@
"search-hint": "Натисни '/'",
"advanced": "Разширени",
"auto-search": "Автоматично търсене",
"no-results": "No results found"
"no-results": "Не са намерени резултати"
},
"settings": {
"add-a-new-theme": "Добавяне на нова тема",
"admin-settings": "Административни настройки",
"backup": {
"backup-created": "Backup created successfully",
"backup-created": "Архивът е създаден успешно",
"backup-created-at-response-export_path": "Резервно копие е създадено на {path}",
"backup-deleted": "Резервното копие е изтрито",
"restore-success": "Restore successful",
"restore-success": "Успешно възстановяване",
"backup-tag": "Таг на резервното копие",
"create-heading": "Създай резервно копие",
"delete-backup": "Изтрий резервно копие",
@@ -690,13 +698,13 @@
"configuration": "Конфигурация",
"docker-volume": "Docker том",
"docker-volume-help": "Mealie изисква контейнерът на frontend и backend да споделят един и същ том на docker или място за съхранение. Това гарантира, че frontend контейнера може да има правилен достъп до изображенията и активите, съхранени на диска.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-misconfigured": "Томовете са конфигурирани неправилно.",
"volumes-are-configured-correctly": "Томовете са конфигурирани правилно.",
"status-unknown-try-running-a-validation": "Статус Неизвестен. Опитайте да стартирате проверка.",
"validate": "Валидирайте",
"email-configuration-status": "Статус на имейл конфигурация",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configured": "Email е конфигуриран",
"email-test-results": "Резултати от тест на email",
"ready": "Готов",
"not-ready": "Не е готово - Проверете променливите на средата",
"succeeded": "Успешно",
@@ -787,7 +795,8 @@
"tags": "Тагове",
"untagged-count": "Без таг {count}",
"create-a-tag": "Създаване на таг",
"tag-name": "Име на тага"
"tag-name": "Име на тага",
"tag": "Тагове"
},
"tool": {
"tools": "Инструменти",
@@ -796,7 +805,8 @@
"tool-name": "Име на инструмента",
"create-new-tool": "Създаване на нов инструмент",
"on-hand-checkbox-label": "Показване като налични (отметнато)",
"required-tools": "Задължителни инструменти"
"required-tools": "Задължителни инструменти",
"tool": "Инструменти"
},
"user": {
"admin": "Админ",
@@ -831,7 +841,7 @@
"password-updated": "Паролата е актуализирана",
"password": "Парола",
"password-strength": "Сигурността на паролата е {strength}",
"please-enter-password": "Please enter your new password.",
"please-enter-password": "Моля, въведете новата си парола.",
"register": "Регистриране",
"reset-password": "Нулиране на паролата",
"sign-in": "Влизане",
@@ -852,7 +862,7 @@
"username": "Потребителско име",
"users-header": "Потребители",
"users": "Потребители",
"user-not-found": "User not found",
"user-not-found": "Потребителят не е намерен",
"webhook-time": "Webhook време",
"webhooks-enabled": "Webhooks са пуснати",
"you-are-not-allowed-to-create-a-user": "Нямате право да създавате потребител",
@@ -875,7 +885,7 @@
"user-management": "Управление на потребителя",
"reset-locked-users": "Нулиране на заключените потребители",
"admin-user-creation": "Създаване на администратор",
"admin-user-management": "Admin User Management",
"admin-user-management": "Управление на администраторите",
"user-details": "Детайли за потребителя",
"user-name": "Потребителско име",
"authentication-method": "Метод за автентикация",
@@ -886,11 +896,11 @@
"user-can-manage-group": "Потребителя може да управлява групата",
"user-can-organize-group-data": "Потребителя може да организира данните на групата",
"enable-advanced-features": "Включване на разширени функции",
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Don't want to see this anymore? Be sure to change your email in your user settings!",
"forgot-password": "Forgot Password",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
"it-looks-like-this-is-your-first-time-logging-in": "Изглежда това е първият път, в който влизате.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Искате ли да виждате това по-често? Уверете се, че сте конфигурирали настройките си за email известяване правилно!",
"forgot-password": "Забравена Парола",
"forgot-password-text": "Въведете Вашият имейл адрес и ние ще ви изпратим линк, с който да промените Вашата парола.",
"changes-reflected-immediately": "Промените по този потребител ще бъдат отразени моментално."
},
"language-dialog": {
"translated": "преведено",
@@ -912,8 +922,8 @@
"food-label": "Заглавие на храната",
"edit-food": "Редактирай храна",
"food-data": "Данни за храните",
"example-food-singular": "ex: Onion",
"example-food-plural": "ex: Onions"
"example-food-singular": "пример: Домат",
"example-food-plural": "пример: Домати"
},
"units": {
"seed-dialog-text": "Заредете базата данни с общи единици въз основа на Вашия местен език.",
@@ -924,7 +934,7 @@
"merging-unit-into-unit": "Обединяване на {0} с {1}",
"create-unit": "Създаване на мерна единица",
"abbreviation": "Абревиатура",
"plural-abbreviation": "Plural Abbreviation",
"plural-abbreviation": "Съкращение за множествено число",
"description": "Описание",
"display-as-fraction": "Показване като фракция",
"use-abbreviation": "Използвай съкращение",
@@ -932,10 +942,10 @@
"unit-data": "Данни на мерната единица",
"use-abbv": "Използвай съкращение",
"fraction": "Фракция",
"example-unit-singular": "ex: Tablespoon",
"example-unit-plural": "ex: Tablespoons",
"example-unit-abbreviation-singular": "ex: Tbsp",
"example-unit-abbreviation-plural": "ex: Tbsps"
"example-unit-singular": "пример: Чаена лъжичка",
"example-unit-plural": "пример: Чаени лъжички",
"example-unit-abbreviation-singular": "пример: ч.л.",
"example-unit-abbreviation-plural": "пример: ч.л.-ки"
},
"labels": {
"seed-dialog-text": "Заредете базата данни с общи етикети въз основа на Вашия местен език.",
@@ -964,8 +974,8 @@
"delete-recipes": "Изтрий рецепти",
"source-unit-will-be-deleted": "Изходната мерна единица ще бъде изтрита"
},
"create-alias": "Create Alias",
"manage-aliases": "Manage Aliases",
"create-alias": "Създаване на псевдоним",
"manage-aliases": "Управление на псевдоними",
"seed-data": "Сийд на данни",
"seed": "Сийд",
"data-management": "Управление на данни",
@@ -975,24 +985,24 @@
"columns": "Колони",
"combine": "Обедини",
"categories": {
"edit-category": "Edit Category",
"new-category": "New Category",
"category-data": "Category Data"
"edit-category": "Редактиране на категория",
"new-category": "Нова категория",
"category-data": "Категория за данните"
},
"tags": {
"new-tag": "New Tag",
"edit-tag": "Edit Tag",
"tag-data": "Tag Data"
"new-tag": "Нов таг",
"edit-tag": "Редакция на таг",
"tag-data": "Данни на тага"
},
"tools": {
"new-tool": "New Tool",
"edit-tool": "Edit Tool",
"tool-data": "Tool Data"
"new-tool": "Нов инструмент",
"edit-tool": "Редактирай инструмента",
"tool-data": "Данни на инструмента"
}
},
"user-registration": {
"user-registration": "Регистрации на потребител",
"registration-success": "Registration Success",
"registration-success": "Успешна регистрация",
"join-a-group": "Присъединете се към групата",
"create-a-new-group": "Създай нова група",
"provide-registration-token-description": "Моля, предоставете регистрационния маркер, свързан с групата, към която искате да се присъедините. Ще трябва да го получите от съществуващ член на групата.",
@@ -1039,7 +1049,7 @@
},
"ocr-editor": {
"ocr-editor": "Ocr редактор",
"toolbar": "Toolbar",
"toolbar": "Лента с инструменти",
"selection-mode": "Режим на избиране",
"pan-and-zoom-picture": "Мащабиране на изображение",
"split-text": "Раздели текст",
@@ -1047,8 +1057,8 @@
"split-by-block": "Раздели по текстов блок",
"flatten": "Изравняване независимо от оригиналното форматиране",
"help": {
"help": "Help",
"mouse-modes": "Mouse modes",
"help": "Помощ",
"mouse-modes": "Режими на мишката",
"selection-mode": "Режим на избиране (по подразбиране)",
"selection-mode-desc": "Режимът за избиране е основният режим, който може да се използва за въвеждане на данни:",
"selection-mode-steps": {

View File

@@ -56,7 +56,7 @@
"event-delete-confirmation": "Està segur que vol suprimir aquest esdeveniment?",
"event-deleted": "Esdeveniment eliminat",
"event-updated": "Esdeveniment actualitzat",
"new-notification-form-description": "Mealie utilitza la llibreria Apprise per a generar notificacions. Ofereix moltes opcions de serveis de notificació. A la seua wiki, disposeu de guies d'ús i informació per a crear l'URL al vostre servei. Si està disponible, al seleccionar el tipus de notificació, pot incloure funcions adicionals.",
"new-notification-form-description": "Mealie utilitza la llibreria Apprise per a generar notificacions. Ofereix moltes opcions de serveis de notificació. A la seva wiki, disposeu de guies d'ús i informació per a crear l'URL al vostre servei. Si està disponible, en seleccionar el tipus de notificació, pot incloure funcions addicionals.",
"new-version": "Hi ha una nova versió disponible!",
"notification": "Notificacions",
"refresh": "Recarrega",
@@ -77,7 +77,7 @@
"tag-events": "Esdeveniments de les etiquetes",
"category-events": "Esdeveniments de les categories",
"when-a-new-user-joins-your-group": "Quan un nou usuari s'afegeix al grup",
"recipe-events": "Recipe Events"
"recipe-events": "Esdeveniments de receptes"
},
"general": {
"cancel": "Anuŀla",
@@ -114,9 +114,10 @@
"json": "JSON",
"keyword": "Paraula clau",
"link-copied": "S'ha copiat l'enllaç",
"loading": "Carregant",
"loading-events": "Carregant esdeveniments",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Carregant la recepta...",
"loading-ocr-data": "Carregant les dades OCR...",
"loading-recipes": "Carregant les receptes",
"message": "Missatge",
"monday": "Dilluns",
@@ -127,7 +128,7 @@
"no-recipe-found": "No s'han trobat receptes",
"ok": "D'acord",
"options": "Opcions:",
"plural-name": "Plural Name",
"plural-name": "Nom en plural",
"print": "Imprimiu",
"print-preferences": "Imprimiu les preferències",
"random": "Aleatori",
@@ -197,7 +198,8 @@
"refresh": "Actualitza",
"upload-file": "Puja un fitxer",
"created-on-date": "Creat el: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "Tens canvis que no estan guardats. Vols guardar-los abans de sortir? Clica d'acord per guardar-los o cancel·lar per descartar els canvis.",
"clipboard-copy-failure": "No s'ha pogut copiar al porta-retalls."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Esteu segur de voler suprimir el grup <b>{groupName}<b/>?",
@@ -212,7 +214,7 @@
"group-id-with-value": "Identificador del grup: {groupID}",
"group-name": "Nom del grup",
"group-not-found": "No s'ha trobat el grup",
"group-token": "Group Token",
"group-token": "Token del grup",
"group-with-value": "Grup: {groupID}",
"groups": "Grups",
"manage-groups": "Gestiona els grups",
@@ -246,16 +248,17 @@
"disable-organizing-recipe-ingredients-by-units-and-food": "Desactiva l'organització dels ingredients de la recepta per unitats i aliments",
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Amaga els camps Aliment, Unitat i Quantitat dels ingredients i tracta els ingredients com a camps de text sense format.",
"general-preferences": "Preferències generals",
"group-recipe-preferences": "Group Recipe Preferences",
"report": "Report",
"report-with-id": "Report ID: {id}",
"group-recipe-preferences": "Preferències del grup de receptes",
"report": "Informe",
"report-with-id": "ID de l'informe: {id}",
"group-management": "Gestió de grups",
"admin-group-management": "Gestió del grup d'administradors",
"admin-group-management-text": "Changes to this group will be reflected immediately.",
"group-id-value": "Group Id: {0}"
"admin-group-management-text": "Els canvis en aquest grup s'actualitzaran immediatament.",
"group-id-value": "ID del grup: {0}"
},
"meal-plan": {
"create-a-new-meal-plan": "Crea un nou menú",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Sopar d'esta setmana",
"dinner-today": "Sopar per avui",
"dinner-tonight": "Sopar d'aquesta nit",
@@ -291,68 +294,68 @@
"editor": "Editor",
"meal-recipe": "Recepta del menú",
"meal-title": "Títol del menú",
"meal-note": "Meal Note",
"note-only": "Note Only",
"meal-note": "Notes del menú",
"note-only": "Només notes",
"random-meal": "Menú aleatori",
"random-dinner": "Principal aleatori",
"random-side": "Guarnició aleatòria",
"this-rule-will-apply": "This rule will apply {dayCriteria} {mealTypeCriteria}.",
"this-rule-will-apply": "Aquesta regla s'aplicarà {dayCriteria} {mealTypeCriteria}.",
"to-all-days": "a tots els dies",
"on-days": "on {0}s",
"on-days": "en {0}s",
"for-all-meal-types": "per a tots els tipus de menús",
"for-type-meal-types": "for {0} meal types",
"meal-plan-rules": "Meal Plan Rules",
"new-rule": "New Rule",
"for-type-meal-types": "per {0} tipus de menús",
"meal-plan-rules": "Normes del planificador de menús",
"new-rule": "Nova norma",
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the categories of the rules will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
"new-rule-description": "When creating a new rule for a meal plan you can restrict the rule to be applicable for a specific day of the week and/or a specific type of meal. To apply a rule to all days or all meal types you can set the rule to \"Any\" which will apply it to all the possible values for the day and/or meal type.",
"recipe-rules": "Recipe Rules",
"recipe-rules": "Normes per la recepta",
"applies-to-all-days": "Aplica a tots els dies",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"applies-on-days": "S'aplicarà en {0}s",
"meal-plan-settings": "Opcions de planificació de menús"
},
"migration": {
"migration-data-removed": "S'han suprimit les dades migrades",
"new-migration": "New Migration",
"new-migration": "Nova migració",
"no-file-selected": "Cap fitxer seleccionat",
"no-migration-data-available": "No hi han dades disponibles",
"previous-migrations": "Previous Migrations",
"previous-migrations": "Migracions prèvies",
"recipe-migration": "Migració de receptes",
"chowdown": {
"description": "Migreu les dades de Chowdown",
"description-long": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below.",
"description-long": "Mealie suporta de forma nativa el format de Chowdown. Descarrega el codi del repositori com a .zip i carrega'l a sota.",
"title": "Chowdown"
},
"nextcloud": {
"description": "Migreu les dades des d'una instància de Nextcloud Cookbook",
"description-long": "Nextcloud recipes can be imported from a zip file that contains the data stored in Nextcloud. See the example folder structure below to ensure your recipes are able to be imported.",
"description-long": "Les receptes de Nextcloud es poden importar des d'un zip que contingui les dades guardades a Nextcloud. Com a exemple, observa l'estructura de carpetes de sota per assegurar-te que les teves receptes es poden importar.",
"title": "Nextcloud Cookbook"
},
"copymethat": {
"description-long": "Mealie pot importar receptes de Copy Me That. Exporta les teves receptes en format HTML i llavors puja el fitxer .zip aquí sota.",
"title": "Copy Me That Recipe Manager"
"title": "Copia aquest gestor de receptes"
},
"paprika": {
"description-long": "Mealie can import recipes from the Paprika application. Export your recipes from paprika, rename the export extension to .zip and upload it below.",
"description-long": "Mealie pot importar receptes de l'aplicació Paprika. Exporta les teves receptes de Paprika, reanomena l'arxiu a format .zip i carrega'l a sota.",
"title": "Paprika Recipe Manager"
},
"mealie-pre-v1": {
"description-long": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.",
"description-long": "Mealie pot importar les receptes des de versions prèvies a la v1.0 d'aquesta aplicació. Exporta les teves receptes des de la teva antiga instància i carrega l'arxiu. zip a sota. Només s'importaran les receptes, i cap dada més.",
"title": "Mealie Pre v1.0"
},
"tandoor": {
"description-long": "Mealie can import recipes from Tandoor. Export your data in the \"Default\" format, then upload the .zip below.",
"description-long": "Mealie pot importar les receptes de Tandoor. Exporta les dades en format \"Default\", i carrega el .zip a sota.",
"title": "Tandoor Recipes"
},
"recipe-data-migrations": "Recipe Data Migrations",
"recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.",
"recipe-data-migrations": "Migració de receptes",
"recipe-data-migrations-explanation": "Les receptes es poden migrar des d'una altra aplicació suportada cap a Mealie. És una manera genial de començar a utilitzar el Mealie.",
"choose-migration-type": "Elegeix un tipus de migració",
"tag-all-recipes": "Etiqueta totes les receptes amb {tag-name}",
"nextcloud-text": "Les receptes de Nextcloud poden ser importades d'un fitxer ZIP que contingui les dades emmagatzemades en Nextcloud. Segueix l'exemple d'estructura de directori de sota per assegurar que les receptes podran ser importades.",
"chowdown-text": "Mealie natively supports the chowdown repository format. Download the code repository as a .zip file and upload it below",
"chowdown-text": "Mealie suporta de forma nativa el format de Chowdown. Descarrega el codi del repositori com a .zip i carrega'l a sota",
"recipe-1": "Recepta 1",
"recipe-2": "Recepta 2",
"paprika-text": "Mealie pot importar receptes des de l'aplicació Paprika. Exporta les teves receptes de Paprika, reanomena l'extensió de l'arxiu a .zip i penja'l aquí sota.",
"mealie-text": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.",
"mealie-text": "Mealie pot importar les receptes des de versions prèvies a la v1.0 d'aquesta aplicació. Exporta les teves receptes des de la teva antiga instància i carrega l'arxiu. zip a sota. Només s'importaran les receptes, i cap dada més.",
"plantoeat": {
"title": "Plan to Eat",
"description-long": "Mealie pot importar receptes de Plan to Eat."
@@ -469,10 +472,12 @@
"add-to-plan": "Afegiu al menú",
"add-to-timeline": "Afegir a la cronologia",
"recipe-added-to-list": "Recepta afegida a la llista",
"recipes-added-to-list": "Recipes added to list",
"recipes-added-to-list": "Receptes afegides a la llista",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recepta afegida al menú",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipes-to-list": "S'ha produït un error al intentar afegir la recepta a la llista",
"failed-to-add-recipe-to-mealplan": "S'ha produït un error afegint la recepta al menú",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Racions",
"quantity": "Quantitat",
"choose-unit": "Tria el tipus d'unitat",
@@ -508,52 +513,55 @@
"made-this": "Ho he fet",
"how-did-it-turn-out": "Com ha sortit?",
"user-made-this": "{user} ha fet això",
"last-made-date": "Last Made {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
"drop-image": "Drop image",
"enable-ingredient-amounts-to-use-this-feature": "Enable ingredient amounts to use this feature",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Recipes with units or foods defined cannot be parsed.",
"parse-ingredients": "Parse ingredients",
"last-made-date": "Última vegada feta {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Clau del missatge",
"parse": "Analitzar",
"attach-images-hint": "Afegeix imatges arrossegant i deixant anar la imatge a l'editor",
"drop-image": "Deixa anar la imatge",
"enable-ingredient-amounts-to-use-this-feature": "Habilita les quantitats d'ingredients per a poder fer servir aquesta característica",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Les receptes amb unitats o aliments definits no es poden analitzar.",
"parse-ingredients": "Analitzar ingredients",
"edit-markdown": "Editar Markdown",
"recipe-creation": "Creació d'una recepta",
"select-one-of-the-various-ways-to-create-a-recipe": "Selecciona una de les diverses formes de crear una recepta",
"looking-for-migrations": "Looking For Migrations?",
"looking-for-migrations": "Estàs buscant migracions?",
"import-with-url": "Importar amb l'URL",
"create-recipe": "Crea la recepta",
"import-with-zip": "Importar amb un .zip",
"create-recipe-from-an-image": "Crea la recepta a partir d'una imatge",
"bulk-url-import": "Importació d'URL en massa",
"debug-scraper": "Debug Scraper",
"debug-scraper": "Rastrejador de depuració",
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Crea la recepta proporcionant-ne un nom. Totes les receptes han de tenir un nom únic.",
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"import-original-keywords-as-tags": "Import original keywords as tags",
"new-recipe-names-must-be-unique": "Els noms de les noves receptes han de ser únics",
"scrape-recipe": "Rastrejar recepta",
"scrape-recipe-description": "Rastrejar recepta des de l'Url. Proporciona un Url del lloc que vols rastrejar i Mealie intentarà analitzar la recepta del lloc web i afegir-la a la teva col·lecció.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Importa les paraules clau originals com a tags",
"stay-in-edit-mode": "Segueix en el mode d'edició",
"import-from-zip": "Importa des d'un ZIP",
"import-from-zip-description": "Import a single recipe that was exported from another Mealie instance.",
"import-from-zip-description": "Importa una sola recepta que ha estat importada d'una altra instància de Mealie.",
"zip-files-must-have-been-exported-from-mealie": "Els fitxers .zip han d'haver sigut exportats des de Mealie",
"create-a-recipe-by-uploading-a-scan": "Crea la recepta pujant-ne un escaneig.",
"upload-a-png-image-from-a-recipe-book": "Puja una imatge PNG d'un llibre de receptes",
"recipe-bulk-importer": "Importador de receptes en massa",
"recipe-bulk-importer-description": "The Bulk recipe importer allows you to import multiple recipes at once by queueing the sites on the backend and running the task in the background. This can be useful when initially migrating to Mealie, or when you want to import a large number of recipes.",
"set-categories-and-tags": "Set Categories and Tags",
"bulk-imports": "Bulk Imports",
"bulk-import-process-has-started": "Bulk Import process has started",
"bulk-import-process-has-failed": "Bulk import process has failed",
"report-deletion-failed": "Report deletion failed",
"recipe-debugger": "Recipe Debugger",
"recipe-debugger-description": "Grab the URL of the recipe you want to debug and paste it here. The URL will be scraped by the recipe scraper and the results will be displayed. If you don't see any data returned, the site you are trying to scrape is not supported by Mealie or its scraper library.",
"debug": "Debug",
"tree-view": "Tree View",
"recipe-yield": "Recipe Yield",
"unit": "Unit",
"upload-image": "Upload image",
"recipe-bulk-importer-description": "L'importador de receptes a granel, permet que importis múltiples receptes a la vegada posant a la cua els llocs al backend i executant la tasca en segon pla. Això pot ser útil si es migra inicialment a Mealie o quan es vol importar un gran nombre de receptes.",
"set-categories-and-tags": "Estableix Categories i Etiquetes",
"bulk-imports": "Importacions a granel",
"bulk-import-process-has-started": "El procés d'importació a granel ha començat",
"bulk-import-process-has-failed": "El procés d'importació a granel ha fallat",
"report-deletion-failed": "No s'ha pogut suprimir l'informe",
"recipe-debugger": "Depuradora de receptes",
"recipe-debugger-description": "Agafa l'URL de la recepta que vols depurar i enganxa-la aquí. L'URL serà reastrejada pel rastrejador de receptes i es mostraran els resultats. Si no veieu cap dada retornada, el lloc que esteu provant de rastrejar no és compatible amb Mealie ni la seva biblioteca de rastreig.",
"debug": "Depuració",
"tree-view": "Vista en arbre",
"recipe-yield": "Rendiment de la recepta",
"unit": "Unitat",
"upload-image": "Puja una imatge",
"screen-awake": "Mantenir la pantalla encesa",
"remove-image": "Esborrar la imatge"
"remove-image": "Esborrar la imatge",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Cerca avançada",
@@ -562,26 +570,26 @@
"include": "Inclou",
"max-results": "No mostreu més de",
"or": "O",
"has-any": "Has Any",
"has-all": "Has All",
"has-any": "Conté qualsevol",
"has-all": "Ho conté tot",
"results": "Resultats",
"search": "Cerca",
"search-mealie": "Cerca a Melie (prem /)",
"search-placeholder": "Cerca...",
"tag-filter": "Filtra per etiqueta",
"search-hint": "Prem '/'",
"advanced": "Advanced",
"auto-search": "Auto Search",
"no-results": "No results found"
"advanced": "Avançat",
"auto-search": "Cerca automàtica",
"no-results": "No s'han trobat resultats"
},
"settings": {
"add-a-new-theme": "Afegiu un nou tema",
"admin-settings": "Opcions de l'administrador",
"backup": {
"backup-created": "Backup created successfully",
"backup-created": "La còpia de seguretat s'ha creat correctament",
"backup-created-at-response-export_path": "S'ha creat una còpia de seguretat a {path}",
"backup-deleted": "Còpia de seguretat suprimida",
"restore-success": "Restore successful",
"restore-success": "La restauració s'ha dut a terme correctament",
"backup-tag": "Etiqueta de la còpia de seguretat",
"create-heading": "Crea una còpia de seguretat",
"delete-backup": "Esborra la còpia de seguretat",
@@ -591,13 +599,13 @@
"partial-backup": "Còpia de seguretat parcial",
"unable-to-delete-backup": "No s'ha pogut suprimir la còpia.",
"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.",
"backup-restore": "Backup Restore",
"back-restore-description": "Restoring this backup will overwrite all the current data in your database and in the data directory and replace them with the contents of this backup. {cannot-be-undone} If the restoration is successful, you will be logged out.",
"cannot-be-undone": "This action cannot be undone - use with caution.",
"postgresql-note": "If you are using PostGreSQL, please review the {backup-restore-process} prior to restoring.",
"backup-restore-process-in-the-documentation": "backup/restore process in the documentation",
"irreversible-acknowledgment": "I understand that this action is irreversible, destructive and may cause data loss",
"restore-backup": "Restore Backup"
"backup-restore": "Restaura la còpia de seguretat",
"back-restore-description": "Restaurar aquesta còpia de seguretat sobreescriurà totes les dades actuals de la teva base de dades i qualsevol directori i els substituirà amb el contingut d'aquesta còpia de seguretat. {cannot-be-undone} Si la restauració es duu a terme correctament, se us tancarà la sessió.",
"cannot-be-undone": "Aquesta acció no es pot desfer. Utilitza-la amb precaució.",
"postgresql-note": "Si estàs fent servir PostGresSQL, si us plau, revisa el {backup-restore-process} abans de fer la restauració.",
"backup-restore-process-in-the-documentation": "el procés de còpia de seguretat i restauració es troba a la documentació",
"irreversible-acknowledgment": "Entenc que aquesta acció és irreversible, destructiva i pot ocasionar la pèrdua de dades",
"restore-backup": "Restaura la còpia de seguretat"
},
"backup-and-exports": "Còpies de seguretat",
"change-password": "Canvia la contrasenya",
@@ -661,8 +669,8 @@
"copy-this-token-for-use-with-an-external-application-this-token-will-not-be-viewable-again": "Còpia aquest token per a utilitzar-lo en una aplicació externa. Aquest token, no es tornarà a mostrar.",
"create-an-api-token": "Crea un token d'API",
"token-name": "Nom del token",
"generate": "Generate",
"you-have-token-count": "You have no active tokens.|You have one active token.|You have {count} active tokens."
"generate": "Genera",
"you-have-token-count": "No tens fitxes actives.|Tens una fitxa activa.|Tens {count} fitxes actives."
},
"toolbox": {
"assign-all": "Asigna tots",
@@ -682,7 +690,7 @@
"webhooks-caps": "WEBHOOKS",
"webhooks": "Webhooks",
"webhook-name": "Nom del Webhook",
"description": "The webhooks defined below will be executed when a meal is defined for the day. At the scheduled time the webhooks will be sent with the data from the recipe that is scheduled for the day. Note that webhook execution is not exact. The webhooks are executed on a 5 minutes interval so the webhooks will be executed within 5 +/- minutes of the scheduled."
"description": "Els webhooks definits a sota s'executaran quan es defineixi un menú pel dia. En un temps estipulat, els webhooks s'enviaran amb les dades de la recepta que estigui programada pel dia. L'execució dels webhooks no és exacta. Els webhooks s'executen en un interval de 5 minuts, és a dir, que s'executaran en un interval de +/- 5 minuts del temps estipulat."
},
"bug-report": "Bug Report",
"bug-report-information": "Use this information to report a bug. Providing details of your instance to developers is the best way to get your issues resolved quickly.",
@@ -694,20 +702,20 @@
"volumes-are-configured-correctly": "Volumes are configured correctly.",
"status-unknown-try-running-a-validation": "Status Unknown. Try running a validation.",
"validate": "Validate",
"email-configuration-status": "Email Configuration Status",
"email-configured": "Email Configured",
"email-configuration-status": "Estat de la configuració del correu electrònic",
"email-configured": "Correu electrònic configurat",
"email-test-results": "Email Test Results",
"ready": "Ready",
"not-ready": "Not Ready - Check Environmental Variables",
"succeeded": "Succeeded",
"failed": "Failed",
"general-about": "General About",
"application-version": "Application Version",
"application-version-error-text": "Your current version ({0}) does not match the latest release. Considering updating to the latest version ({1}).",
"mealie-is-up-to-date": "Mealie is up to date",
"secure-site": "Secure Site",
"secure-site-error-text": "Serve via localhost or secure with https. Clipboard and additional browser APIs may not work.",
"secure-site-success-text": "Site is accessed by localhost or https",
"succeeded": "Va tenir èxit",
"failed": "Ha fallat",
"general-about": "Informació General",
"application-version": "Versió de l'Aplicació",
"application-version-error-text": "La teva versió actual ({0}) no correspon amb l'última publicada. Considera actualitzar a l'última versió ({1}).",
"mealie-is-up-to-date": "Mealie està actualitzat",
"secure-site": "Web Segur",
"secure-site-error-text": "Allotja mitjançant localhost o assegura amb https. És possible que el porta-retalls i API addicionals del navegador no funcionin.",
"secure-site-success-text": "El web és accedit amb localhost o https",
"server-side-base-url": "Server Side Base URL",
"server-side-base-url-error-text": "`BASE_URL` is still the default value on API Server. This will cause issues with notifications links generated on the server for emails, etc.",
"server-side-base-url-success-text": "Server Side URL does not match the default",
@@ -731,19 +739,19 @@
"label": "Etiqueta",
"linked-item-warning": "Aquest element està enllaçat amb una o més receptes. Modificar les unitats o els aliments pot provocar resultats inesperats en afegir o elimina la recepta del llistat.",
"toggle-food": "Mostra el nom de l'aliment",
"manage-labels": "Manage Labels",
"are-you-sure-you-want-to-delete-this-item": "Are you sure you want to delete this item?",
"copy-as-text": "Copy as Text",
"copy-as-markdown": "Copy as Markdown",
"delete-checked": "Delete Checked",
"toggle-label-sort": "Toggle Label Sort",
"reorder-labels": "Reorder Labels",
"uncheck-all-items": "Uncheck All Items",
"check-all-items": "Check All Items",
"manage-labels": "Gestiona etiquetes",
"are-you-sure-you-want-to-delete-this-item": "Estàs segur/a que vols eliminar aquest ítem?",
"copy-as-text": "Copia com a text",
"copy-as-markdown": "Copia com a Markdown",
"delete-checked": "Suprimeix la selecció",
"toggle-label-sort": "Activa/Desactiva l'ordre per etiquetes",
"reorder-labels": "Canvia d'ordre les etiquetes",
"uncheck-all-items": "Desselecciona tots els ítems",
"check-all-items": "Selecciona tots els ítems",
"linked-recipes-count": "No Linked Recipes|One Linked Recipe|{count} Linked Recipes",
"items-checked-count": "No items checked|One item checked|{count} items checked",
"no-label": "No Label",
"completed-on": "Completed on {date}"
"no-label": "Sense etiqueta",
"completed-on": "Completat el {date}"
},
"sidebar": {
"all-recipes": "Receptes",
@@ -787,7 +795,8 @@
"tags": "Etiquetes",
"untagged-count": "{count} sense etiquetar",
"create-a-tag": "Crea una etiqueta",
"tag-name": "Nom de l'etiqueta"
"tag-name": "Nom de l'etiqueta",
"tag": "Tag"
},
"tool": {
"tools": "Estris",
@@ -796,13 +805,14 @@
"tool-name": "Nom de l'estri",
"create-new-tool": "Crea un nou estri",
"on-hand-checkbox-label": "Mostra com a disponible (marcat)",
"required-tools": "Required Tools"
"required-tools": "Eines necessàries",
"tool": "Tool"
},
"user": {
"admin": "Administrador/a",
"are-you-sure-you-want-to-delete-the-link": "Esteu segur de voler suprimir l'enllaç <b>{link}<b/>?",
"are-you-sure-you-want-to-delete-the-user": "Esteu segur de voler suprimir l'usuari <b>{activeName}<b/> ID: <b>{activeId}<b/>?",
"auth-method": "Auth Method",
"auth-method": "Mètode d'autenticació",
"confirm-link-deletion": "Confirmeu l'eliminació de l'enllaç",
"confirm-password": "Confirmeu la contrasenya",
"confirm-user-deletion": "Confirmeu l'eliminació de l'usuari",
@@ -816,7 +826,7 @@
"error-cannot-delete-super-user": "S'ha produït un error. El Super usuari no es pot suprimir!",
"existing-password-does-not-match": "La contrasenya actual no coincideix",
"full-name": "Nom sencer",
"generate-password-reset-link": "Generate Password Reset Link",
"generate-password-reset-link": "Genera un enllaç per reiniciar la contrasenya",
"invite-only": "Només per invitació",
"link-id": "Id de l'enllaç",
"link-name": "Nom de l'enllaç",
@@ -831,7 +841,7 @@
"password-updated": "S'ha actualitzat la contrasenya",
"password": "Contrasenya",
"password-strength": "Fortalesa de la contrasenya: {strength}",
"please-enter-password": "Please enter your new password.",
"please-enter-password": "Si us plau, entra la teva nova contrasenya.",
"register": "Registreu-vos",
"reset-password": "Restableix la contrasenya",
"sign-in": "Inicia sessió",
@@ -852,7 +862,7 @@
"username": "Nom d'usuari",
"users-header": "USUARIS",
"users": "Usuaris",
"user-not-found": "User not found",
"user-not-found": "No s'ha trobat l'usuari",
"webhook-time": "Hora del Webhook",
"webhooks-enabled": "Webhooks habilitats",
"you-are-not-allowed-to-create-a-user": "Vostè no està autoritzat per a crear un usuari",
@@ -860,24 +870,24 @@
"enable-advanced-content": "Habilita el contingut avançat",
"enable-advanced-content-description": "Habilita les funcions avançades com ara multiplicar els ingredients, claus API, Webhooks i la gestió de les dades. Pots tornar a canviar aquesta configuració més tard",
"favorite-recipes": "Receptes preferides",
"email-or-username": "Email or Username",
"remember-me": "Remember Me",
"please-enter-your-email-and-password": "Please enter your email and password",
"invalid-credentials": "Invalid Credentials",
"account-locked-please-try-again-later": "Account Locked. Please try again later",
"user-favorites": "User Favorites",
"email-or-username": "Correu electrònic o nom d'usuari",
"remember-me": "Recorda'm",
"please-enter-your-email-and-password": "Si us plau, introdueix el teu correu electrònic i la teva contrasenya",
"invalid-credentials": "Credencials no vàlides",
"account-locked-please-try-again-later": "Compte bloquejat, Si us plau, prova-ho més tard",
"user-favorites": "Favorits de l'usuari",
"password-strength-values": {
"weak": "Weak",
"good": "Good",
"strong": "Strong",
"very-strong": "Very Strong"
"weak": "Dèbil",
"good": "Bona",
"strong": "Forta",
"very-strong": "Molt forta"
},
"user-management": "User Management",
"reset-locked-users": "Reset Locked Users",
"admin-user-creation": "Admin User Creation",
"user-management": "Gestió d'usuaris",
"reset-locked-users": "Reinicia els usuaris bloquejats",
"admin-user-creation": "Creació d'un usuari administrador",
"admin-user-management": "Admin User Management",
"user-details": "User Details",
"user-name": "User Name",
"user-details": "Detalls de l'usuari",
"user-name": "Nom de l'usuari",
"authentication-method": "Authentication Method",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Permissions",
@@ -888,7 +898,7 @@
"enable-advanced-features": "Enable advanced features",
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Don't want to see this anymore? Be sure to change your email in your user settings!",
"forgot-password": "Forgot Password",
"forgot-password": "Contrasenya oblidada",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
},

View File

@@ -66,7 +66,7 @@
"test-message-sent": "Testovací zpráva odeslána",
"new-notification": "Nové oznámení",
"event-notifiers": "Notifikace událostí",
"apprise-url-skipped-if-blank": "Apprise URL (skipped if blank)",
"apprise-url-skipped-if-blank": "Apprise URL (přeskočeno pokud je prázdné)",
"enable-notifier": "Povolit notifikaci",
"what-events": "What events should this notifier subscribe to?",
"user-events": "Uživatelské události",
@@ -114,9 +114,10 @@
"json": "JSON",
"keyword": "Klíčové slovo",
"link-copied": "Odkaz zkopírován",
"loading": "Načítá se",
"loading-events": "Načítání událostí",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Načítám recept...",
"loading-ocr-data": "Načítám OCR data...",
"loading-recipes": "Načítám recepty",
"message": "Zpráva",
"monday": "Pondělí",
@@ -197,7 +198,8 @@
"refresh": "Obnovit",
"upload-file": "Nahrát soubor",
"created-on-date": "Vytvořeno dne: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Jste si jisti, že chcete smazat <b>{groupName}<b/>?",
@@ -241,8 +243,8 @@
"show-recipe-assets-description": "When enabled the recipe assets will be shown on the recipe if available",
"default-to-landscape-view": "Default to landscape view",
"default-to-landscape-view-description": "When enabled the recipe header section will be shown in landscape view",
"disable-users-from-commenting-on-recipes": "Disable users from commenting on recipes",
"disable-users-from-commenting-on-recipes-description": "Hides the comment section on the recipe page and disables commenting",
"disable-users-from-commenting-on-recipes": "Zakázat uživatelům komentovat u receptů",
"disable-users-from-commenting-on-recipes-description": "Na stránce receptu skryje sekci s komentáři a zakáže komentování",
"disable-organizing-recipe-ingredients-by-units-and-food": "Disable organizing recipe ingredients by units and food",
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields.",
"general-preferences": "General Preferences",
@@ -252,10 +254,11 @@
"group-management": "Správa skupin",
"admin-group-management": "Admin Group Management",
"admin-group-management-text": "Změny v této skupině budou okamžitě zohledněny.",
"group-id-value": "Group Id: {0}"
"group-id-value": "ID skupiny: {0}"
},
"meal-plan": {
"create-a-new-meal-plan": "Vytvořit nový jídelníček",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Večeře na tento týden",
"dinner-today": "Dnešní večeře",
"dinner-tonight": "DNEŠNÍ VEČEŘE",
@@ -305,10 +308,10 @@
"new-rule": "Nové pravidlo",
"meal-plan-rules-description": "You can create rules for auto selecting recipes for your meal plans. These rules are used by the server to determine the random pool of recipes to select from when creating meal plans. Note that if rules have the same day/type constraints then the categories of the rules will be merged. In practice, it's unnecessary to create duplicate rules, but it's possible to do so.",
"new-rule-description": "When creating a new rule for a meal plan you can restrict the rule to be applicable for a specific day of the week and/or a specific type of meal. To apply a rule to all days or all meal types you can set the rule to \"Any\" which will apply it to all the possible values for the day and/or meal type.",
"recipe-rules": "Recipe Rules",
"recipe-rules": "Pravidla receptu",
"applies-to-all-days": "Použije se na všechny dny",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"meal-plan-settings": "Nastavení jídelníčku"
},
"migration": {
"migration-data-removed": "Data z migrace byla smazána",
@@ -333,7 +336,7 @@
},
"paprika": {
"description-long": "Mealie can import recipes from the Paprika application. Export your recipes from paprika, rename the export extension to .zip and upload it below.",
"title": "Paprika Recipe Manager"
"title": "Správce receptů Paprika"
},
"mealie-pre-v1": {
"description-long": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.",
@@ -467,12 +470,14 @@
"date-format-hint-yyyy-mm-dd": "Formát RRRR-MM-DD",
"add-to-list": "Přidat na seznam",
"add-to-plan": "Přidat do jídelníčku",
"add-to-timeline": "Add to Timeline",
"add-to-timeline": "Přidat na časovou osu",
"recipe-added-to-list": "Recept byl přidán na seznam",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recept byl přidán do jídelníčku",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Přidání receptu do jídelníčku selhalo",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Úroda",
"quantity": "Množství",
"choose-unit": "Vybrat jednotku",
@@ -494,9 +499,9 @@
"locked": "Uzamčeno",
"public-link": "Veřejný odkaz",
"timer": {
"kitchen-timer": "Kitchen Timer",
"start-timer": "Start Timer",
"pause-timer": "Pause Timer",
"kitchen-timer": "Kuchyňský časovač",
"start-timer": "Spustit časovač",
"pause-timer": "Pozastavit časovač",
"resume-timer": "Resume Timer",
"stop-timer": "Stop Timer"
},
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Jak to dopadlo?",
"user-made-this": "{user} udělal toto",
"last-made-date": "Naposledy uvařeno {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Import original keywords as tags",
"stay-in-edit-mode": "Zůstat v režimu úprav",
"import-from-zip": "Importovat ze zipu",
@@ -553,7 +560,8 @@
"unit": "Jednotka",
"upload-image": "Nahrát obrázek",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Další krok"
},
"search": {
"advanced-search": "Pokročilé vyhledávání",
@@ -787,7 +795,8 @@
"tags": "Štítky",
"untagged-count": "Bez štítku {count}",
"create-a-tag": "Vytvořit štítek",
"tag-name": "Název štítku"
"tag-name": "Název štítku",
"tag": "Tag"
},
"tool": {
"tools": "Nástroje",
@@ -796,7 +805,8 @@
"tool-name": "Název nástroje",
"create-new-tool": "Vytvořit nový nástroj",
"on-hand-checkbox-label": "Zobrazit jako \"Po ruce\" (zaškrtnuto)",
"required-tools": "Required Tools"
"required-tools": "Required Tools",
"tool": "Tool"
},
"user": {
"admin": "Správce",
@@ -1147,7 +1157,7 @@
"notifiers-description": "Setup email and push notifications that trigger on specific events.",
"manage-data": "Spravovat data",
"manage-data-description": "Manage your Food and Units (more options coming soon)",
"data-migrations": "Data Migrations",
"data-migrations": "Migrace dat",
"data-migrations-description": "Migrate your existing data from other applications like Nextcloud Recipes and Chowdown",
"email-sent": "E-mail odeslán",
"error-sending-email": "Nastala chyba při odesílání e-mailu",
@@ -1159,10 +1169,10 @@
"manage-your-api-tokens": "Správa API tokenů",
"manage-user-profile": "Správa uživatelského profilu",
"manage-cookbooks": "Správa kuchařek",
"manage-members": "Manage Members",
"manage-webhooks": "Manage Webhooks",
"manage-notifiers": "Manage Notifiers",
"manage-data-migrations": "Manage Data Migrations"
"manage-members": "Spravovat členy",
"manage-webhooks": "Spravovat webhooky",
"manage-notifiers": "Spravovat oznámení",
"manage-data-migrations": "Spravovat migrace dat"
},
"cookbook": {
"cookbooks": "Kuchařky",
@@ -1170,10 +1180,10 @@
"public-cookbook": "Veřejná kuchařka",
"public-cookbook-description": "Veřejné kuchařky mohou být sdíleny s neregistrovanými uživateli a budou zobrazeny na stránce vaší skupiny.",
"filter-options": "Možnosti filtru",
"filter-options-description": "When require all is selected the cookbook will only include recipes that have all of the items selected. This applies to each subset of selectors and not a cross section of the selected items.",
"require-all-categories": "Require All Categories",
"require-all-tags": "Require All Tags",
"require-all-tools": "Require All Tools",
"filter-options-description": "Pokud je vybrána možnost vyžadovat vše, kuchařka bude obsahovat pouze ty recepty, které mají všechny vybrané položky. To platí pro každou podmnožinu výběru, nikoliv pro jejich průnik.",
"require-all-categories": "Vyžadovat všechny kategorie",
"require-all-tags": "Vyžadovat všechny štítky",
"require-all-tools": "Vyžadovat všechny nástroje",
"cookbook-name": "Název kuchařky",
"cookbook-with-name": "Kuchařka {0}"
}

View File

@@ -77,7 +77,7 @@
"tag-events": "Tag Begivenheder",
"category-events": "Kategori Begivenheder",
"when-a-new-user-joins-your-group": "Når en ny bruger slutter sig til din gruppe",
"recipe-events": "Recipe Events"
"recipe-events": "Hændelser for opskrifter"
},
"general": {
"cancel": "Annuller",
@@ -114,9 +114,10 @@
"json": "JSON",
"keyword": "Nøgleord",
"link-copied": "Link kopieret",
"loading": "Indlæser",
"loading-events": "Indlæser hændelser",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Indlæser opskrift...",
"loading-ocr-data": "Indlæser OCR data...",
"loading-recipes": "Indlæser opskrifter",
"message": "Besked",
"monday": "Mandag",
@@ -197,7 +198,8 @@
"refresh": "Opdater",
"upload-file": "Upload Fil",
"created-on-date": "Oprettet den: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "Du har ændringer som ikke er gemt. Vil du gemme før du forlader? Vælg \"Okay\" for at gemme, eller \"Annullér\" for at kassere ændringer.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Er du sikker på, du vil slette <b>{groupName}<b/>?",
@@ -212,7 +214,7 @@
"group-id-with-value": "Gruppe-ID: {groupID}",
"group-name": "Gruppenavn",
"group-not-found": "Gruppen blev ikke fundet",
"group-token": "Group Token",
"group-token": "Gruppe Token",
"group-with-value": "Gruppe: {groupID}",
"groups": "Grupper",
"manage-groups": "Administrer grupper",
@@ -248,7 +250,7 @@
"general-preferences": "Generelle Indstillinger",
"group-recipe-preferences": "Gruppe Indstillinger for opskrifter",
"report": "Rapport",
"report-with-id": "Report ID: {id}",
"report-with-id": "Rapport ID: {id}",
"group-management": "Gruppe Håndtering",
"admin-group-management": "Administrationsgruppe Håndtering",
"admin-group-management-text": "Ændringer i denne gruppe vil træde i kraft øjeblikkeligt.",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Opret en ny madplan",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Madplan denne uge",
"dinner-today": "Madplan i dag",
"dinner-tonight": "AFTENSMAD I AFTEN",
@@ -470,9 +473,11 @@
"add-to-timeline": "Tilføj til tidslinje",
"recipe-added-to-list": "Opskrift tilføjet til listen",
"recipes-added-to-list": "Opskrifter tilføjet til listen",
"successfully-added-to-list": "Tilføjet til listen",
"recipe-added-to-mealplan": "Opskrift tilføjet til madplanen",
"failed-to-add-recipes-to-list": "Kunne ikke tilføje opskrift til listen",
"failed-to-add-recipe-to-mealplan": "Kunne ikke tilføje opskrift til madplanen",
"failed-to-add-to-list": "Kunne ikke tilføje opskrift til listen",
"yield": "Portioner",
"quantity": "Antal",
"choose-unit": "Vælg enhed",
@@ -509,11 +514,11 @@
"how-did-it-turn-out": "Hvordan blev det?",
"user-made-this": "{user} lavede denne",
"last-made-date": "Sidst tilberedt den {date}",
"api-extras-description": "Brugerdefinerede oplysninger i opskrifter i Mealie's gør det muligt at oprette JSON data bestående af en nøgle og tilhørende værdier. Data udstilles i Mealie's API og kan bruges af andre applikationer til at udløse automatiseringer eller vise brugerdefineret indhold.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Beskednøgle",
"parse": "Behandl data",
"attach-images-hint": "Vedhæft billeder ved at trække dem ind i redigeringsværktøjet",
"drop-image": "Drop image",
"drop-image": "Slet billede",
"enable-ingredient-amounts-to-use-this-feature": "Aktiver mængde af ingredienser for at bruge denne funktion",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "Opskrifter med enheder eller fødevarer defineret kan ikke fortolkes.",
"parse-ingredients": "Fortolk ingredienser",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Opskriftsnavnet er allerede i brug",
"scrape-recipe": "Scrape Opskrift",
"scrape-recipe-description": "Hent en opskrift fra en hjemmeside. Angiv URL'en til den hjemmeside, du vil hente data fra, og Mealie vil forsøge at hente opskriften og tilføje den til din samling.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Importér originale nøgleord som mærker",
"stay-in-edit-mode": "Bliv i redigeringstilstand",
"import-from-zip": "Importer fra zip-fil",
@@ -553,7 +560,8 @@
"unit": "Enhed",
"upload-image": "Upload billede",
"screen-awake": "Hold skærmen tændt",
"remove-image": "Fjern billede"
"remove-image": "Fjern billede",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Avanceret søgning",
@@ -572,16 +580,16 @@
"search-hint": "Tryk '/'",
"advanced": "Avanceret",
"auto-search": "Automatisk Søgning",
"no-results": "No results found"
"no-results": "Ingen resultater fundet"
},
"settings": {
"add-a-new-theme": "Tilføj et nyt tema",
"admin-settings": "Administratorindstillinger",
"backup": {
"backup-created": "Backup created successfully",
"backup-created": "Backup oprettet med succes",
"backup-created-at-response-export_path": "Backup oprettet ved {path}",
"backup-deleted": "Backup slettet",
"restore-success": "Restore successful",
"restore-success": "Gendannelse lykkedes",
"backup-tag": "Backupnavn",
"create-heading": "Opret en backup",
"delete-backup": "Slet backup",
@@ -690,13 +698,13 @@
"configuration": "Konfiguration",
"docker-volume": "Docker Volume",
"docker-volume-help": "Mealie kræver, at frontend og backend containere deler den samme docker mappe. Dette sikrer, at frontend container har adgang til billeder og øvrige lagret på disken.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-misconfigured": "Docker mapper er forkert konfigureret.",
"volumes-are-configured-correctly": "Docker mapper er korrekt konfigureret.",
"status-unknown-try-running-a-validation": "Status Ukendt. Prøv at køre en validering.",
"validate": "Validering",
"email-configuration-status": "Status for konfiguration af e-mail",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configured": "Email Konfigureret",
"email-test-results": "Email Testresultater",
"ready": "Klar",
"not-ready": "Ikke Klar - Kontroller konfigurationen",
"succeeded": "Gennemført",
@@ -787,7 +795,8 @@
"tags": "Tags",
"untagged-count": "Ikke-tagget: {count}",
"create-a-tag": "Opret tag",
"tag-name": "Tag navn"
"tag-name": "Tag navn",
"tag": "Tag"
},
"tool": {
"tools": "Værktøjer",
@@ -796,7 +805,8 @@
"tool-name": "Værktøjsnavn",
"create-new-tool": "Opret et nyt værktøj",
"on-hand-checkbox-label": "Vis som \"Har allerede\" (afkrydset)",
"required-tools": "Nødvendige Værktøjer"
"required-tools": "Nødvendige Værktøjer",
"tool": "Tool"
},
"user": {
"admin": "Administrator",
@@ -831,7 +841,7 @@
"password-updated": "Adgangskoden blev opdateret",
"password": "Adgangskode",
"password-strength": "Adgangskodestyrken er {strength}",
"please-enter-password": "Please enter your new password.",
"please-enter-password": "Indtast venligst din nye adgangskode.",
"register": "Registrér",
"reset-password": "Nulstil adgangskoden",
"sign-in": "Log ind",
@@ -852,7 +862,7 @@
"username": "Brugernavn",
"users-header": "BRUGERE",
"users": "Brugere",
"user-not-found": "User not found",
"user-not-found": "Brugeren kunne ikke findes",
"webhook-time": "Webhook Tid",
"webhooks-enabled": "Webhooks Aktiveret",
"you-are-not-allowed-to-create-a-user": "Du har ikke rettigheder til at oprette en ny bruger",
@@ -875,7 +885,7 @@
"user-management": "Brugeradministration",
"reset-locked-users": "Nulstil Låste Brugere",
"admin-user-creation": "Opret administratorbruger",
"admin-user-management": "Admin User Management",
"admin-user-management": "Håndter administratorbruger",
"user-details": "Brugerdetaljer",
"user-name": "Brugernavn",
"authentication-method": "Godkendelsesmetode",
@@ -888,9 +898,9 @@
"enable-advanced-features": "Aktiver avancerede funktioner",
"it-looks-like-this-is-your-first-time-logging-in": "Det ser ud til, at det er første gang, at du logger ind.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Vil du ikke længere se dette? Sørg for at ændre din e-mail i dine brugerindstillinger!",
"forgot-password": "Forgot Password",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
"forgot-password": "Glemt adgangskode",
"forgot-password-text": "Indtast venligst din e-mail-adresse. Vi sender dig en e-mail, så at du kan nulstille din adgangskode.",
"changes-reflected-immediately": "Ændringer til denne bruger vil have effekt med det samme."
},
"language-dialog": {
"translated": "oversat",
@@ -992,7 +1002,7 @@
},
"user-registration": {
"user-registration": "Brugerregistrering",
"registration-success": "Registration Success",
"registration-success": "Registrering lykkedes",
"join-a-group": "Deltag i en gruppe",
"create-a-new-group": "Opret en ny gruppe",
"provide-registration-token-description": "Angiv venligst det registreringstoken, der er knyttet til den gruppe, du gerne vil deltage i. Du skal indhente dette fra et eksisterende gruppemedlem.",
@@ -1039,7 +1049,7 @@
},
"ocr-editor": {
"ocr-editor": "Ocr redigering",
"toolbar": "Toolbar",
"toolbar": "Værktøjslinje",
"selection-mode": "Markeringstilstand",
"pan-and-zoom-picture": "Panorer og zoom på billede",
"split-text": "Opdel tekst",
@@ -1047,8 +1057,8 @@
"split-by-block": "Opdelt efter tekstblok",
"flatten": "Udjævn på trods af original formatering",
"help": {
"help": "Help",
"mouse-modes": "Mouse modes",
"help": "Hlp",
"mouse-modes": "Muse tilstande",
"selection-mode": "Markeringstilstand (standard)",
"selection-mode-desc": "Markeringstilstanden er den primære tilstand, der kan benyttes til at indtaste data:",
"selection-mode-steps": {

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Schlüsselwort",
"link-copied": "Link kopiert",
"loading": "Wird geladen...",
"loading-events": "Ereignisse werden geladen",
"loading-recipe": "Lade Rezept...",
"loading-ocr-data": "Lade OCR-Daten...",
@@ -197,7 +198,8 @@
"refresh": "Aktualisieren",
"upload-file": "Datei hochladen",
"created-on-date": "Erstellt am: {0}",
"unsaved-changes": "Du hast ungespeicherte Änderungen. Möchtest du vor dem Verlassen speichern? OK um zu speichern, Cancel um Änderungen zu verwerfen."
"unsaved-changes": "Du hast ungespeicherte Änderungen. Möchtest du vor dem Verlassen speichern? OK um zu speichern, Cancel um Änderungen zu verwerfen.",
"clipboard-copy-failure": "Fehler beim Kopieren in die Zwischenablage."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Bist du dir sicher, dass du die Gruppe <b>{groupName}<b/> löschen möchtest?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Neue Mahlzeit planen",
"update-this-meal-plan": "Mahlzeit aktualisieren",
"dinner-this-week": "Essen diese Woche",
"dinner-today": "Heutiges Essen",
"dinner-tonight": "HEUTE GIBT ES",
@@ -470,9 +473,11 @@
"add-to-timeline": "Zum Zeitstrahl hinzufügen",
"recipe-added-to-list": "Rezept wurde zur Einkaufsliste hinzugefügt",
"recipes-added-to-list": "Rezepte wurden zur Einkaufsliste hinzugefügt",
"successfully-added-to-list": "Erfolgreich zur Liste hinzugefügt",
"recipe-added-to-mealplan": "Rezept zum Essensplan hinzugefügt",
"failed-to-add-recipes-to-list": "Fehler beim Hinzufügen des Rezepts zur Einkaufsliste",
"failed-to-add-recipe-to-mealplan": "Fehler beim Hinzufügen des Rezepts zum Essensplan",
"failed-to-add-to-list": "Fehler beim Hinzufügen zur Liste",
"yield": "Portionsangabe",
"quantity": "Menge",
"choose-unit": "Einheit wählen",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Wie ist es geworden?",
"user-made-this": "{user} hat's gemacht",
"last-made-date": "Zuletzt gemacht {date}",
"api-extras-description": "Rezepte-Extras sind ein Hauptmerkmal der Mealie API. Sie ermöglichen es dir, benutzerdefinierte JSON Schlüssel/Wert-Paare zu einem Rezept zu erstellen, um Anwendungen von Drittanbietern zu steuern. Du kannst diese Schlüssel dazu verwenden, Automatisierungen auszulösen oder benutzerdefinierte Nachrichten an deine gewünschten Geräte zu senden.",
"api-extras-description": "Rezepte-Extras sind ein Hauptmerkmal der Mealie API. Sie ermöglichen es dir, benutzerdefinierte JSON Key-Value-Paare zu einem Rezept zu erstellen, um Drittanbietern-Anwendungen zu steuern. Du kannst diese dazu verwenden, um Automatisierungen auszulösen oder benutzerdefinierte Nachrichten an bestimmte Geräte zu senden.",
"message-key": "Nachrichten-Schlüssel",
"parse": "Parse",
"attach-images-hint": "Bilder durch Ziehen & Ablegen in den Editor hinzufügen",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Neue Rezeptnamen müssen eindeutig sein",
"scrape-recipe": "Rezept einlesen",
"scrape-recipe-description": "Importiere ein Rezept mit der URL. Gib die URL für die Seite an, die du importieren möchtest und Mealie wird versuchen, das Rezept von dieser Seite einzulesen und deiner Sammlung hinzuzufügen.",
"scrape-recipe-have-a-lot-of-recipes": "Hast Du viele Rezepte, die Du auf einmal einlesen willst?",
"scrape-recipe-suggest-bulk-importer": "Probiere den Massenimporter aus",
"import-original-keywords-as-tags": "Importiere ursprüngliche Stichwörter als Schlagwörter",
"stay-in-edit-mode": "Im Bearbeitungsmodus bleiben",
"import-from-zip": "Von Zip importieren",
@@ -553,7 +560,8 @@
"unit": "Maßeinheit",
"upload-image": "Bild hochladen",
"screen-awake": "Bildschirm nicht abschalten",
"remove-image": "Bild entfernen"
"remove-image": "Bild entfernen",
"nextStep": "Nächster Schritt"
},
"search": {
"advanced-search": "Erweiterte Suche",
@@ -643,16 +651,16 @@
"primary": "Primär",
"secondary": "Sekundär",
"success": "Erfolg",
"switch-to-dark-mode": "Zum dunklen Modus wechseln",
"switch-to-light-mode": "Zum hellen Modus wechseln",
"switch-to-dark-mode": "Zum dunklen Design wechseln",
"switch-to-light-mode": "Zum hellen Design wechseln",
"theme-deleted": "Thema gelöscht",
"theme-name": "Themenname",
"theme-name-is-required": "Themenname wird benötigt.",
"theme-saved": "Thema gespeichert",
"theme-updated": "Thema aktualisiert",
"warning": "Warnung",
"light-mode": "Heller Modus",
"dark-mode": "Dunkler Modus"
"light-mode": "Helles Design",
"dark-mode": "Dunkles Design"
},
"token": {
"active-tokens": "AKTIVE TOKEN",
@@ -787,7 +795,8 @@
"tags": "Schlagworte",
"untagged-count": "{count} ohne Schlagworte",
"create-a-tag": "Ein Schlagwort erstellen",
"tag-name": "Name des Schlagworts"
"tag-name": "Name des Schlagworts",
"tag": "Schlagwort"
},
"tool": {
"tools": "Utensilien",
@@ -796,7 +805,8 @@
"tool-name": "Name des Utensils",
"create-new-tool": "Neues Utensil erstellen",
"on-hand-checkbox-label": "Als \"vorhanden\" anzeigen (markiert)",
"required-tools": "Benötigte Utensilien"
"required-tools": "Benötigte Utensilien",
"tool": "Utensil"
},
"user": {
"admin": "Admin",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Λέξη-κλειδί",
"link-copied": "Ο Σύνδεσμος Αντιγράφηκε",
"loading": "Loading",
"loading-events": "Loading Events",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Ανανέωση",
"upload-file": "Μεταφόρτωση αρχείου",
"created-on-date": "Δημιουργήθηκε στις: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό τον ασφαλή σύνδεσμο <b>{groupName}<b/>;",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Δημιουργία νέου σχεδίου γεύματος",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Δείπνο Αυτή Τη Εβδομάδα",
"dinner-today": "Δείπνο Σήμερα",
"dinner-tonight": "ΔΕΙΠΝΟ ΣΗΜΕΡΑ",
@@ -470,9 +473,11 @@
"add-to-timeline": "Add to Timeline",
"recipe-added-to-list": "Recipe added to list",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recipe added to mealplan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Failed to add recipe to mealplan",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Yield",
"quantity": "Quantity",
"choose-unit": "Choose Unit",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "How did it turn out?",
"user-made-this": "{user} made this",
"last-made-date": "Last Made {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Import original keywords as tags",
"stay-in-edit-mode": "Stay in Edit mode",
"import-from-zip": "Import from Zip",
@@ -553,7 +560,8 @@
"unit": "Unit",
"upload-image": "Upload image",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Σύνθετη Αναζήτηση",
@@ -787,7 +795,8 @@
"tags": "Ετικέτες",
"untagged-count": "Χωρίς ετικέτα {count}",
"create-a-tag": "Create a Tag",
"tag-name": "Tag Name"
"tag-name": "Tag Name",
"tag": "Tag"
},
"tool": {
"tools": "Εργαλεία",
@@ -796,7 +805,8 @@
"tool-name": "Tool Name",
"create-new-tool": "Create New Tool",
"on-hand-checkbox-label": "Show as On Hand (Checked)",
"required-tools": "Required Tools"
"required-tools": "Required Tools",
"tool": "Tool"
},
"user": {
"admin": "Διαχειριστής",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Keyword",
"link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Create a New Meal Plan",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Dinner This Week",
"dinner-today": "Dinner Today",
"dinner-tonight": "DINNER TONIGHT",
@@ -470,9 +473,11 @@
"add-to-timeline": "Add to Timeline",
"recipe-added-to-list": "Recipe added to list",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recipe added to mealplan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Failed to add recipe to mealplan",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Yield",
"quantity": "Quantity",
"choose-unit": "Choose Unit",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "How did it turn out?",
"user-made-this": "{user} made this",
"last-made-date": "Last Made {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Import original keywords as tags",
"stay-in-edit-mode": "Stay in Edit mode",
"import-from-zip": "Import from Zip",
@@ -553,7 +560,8 @@
"unit": "Unit",
"upload-image": "Upload image",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Advanced Search",
@@ -787,7 +795,8 @@
"tags": "Tags",
"untagged-count": "Untagged {count}",
"create-a-tag": "Create a Tag",
"tag-name": "Tag Name"
"tag-name": "Tag Name",
"tag": "Tag"
},
"tool": {
"tools": "Tools",
@@ -796,7 +805,8 @@
"tool-name": "Tool Name",
"create-new-tool": "Create New Tool",
"on-hand-checkbox-label": "Show as On Hand (Checked)",
"required-tools": "Required Tools"
"required-tools": "Required Tools",
"tool": "Tool"
},
"user": {
"admin": "Admin",
@@ -879,7 +889,7 @@
"user-details": "User Details",
"user-name": "User Name",
"authentication-method": "Authentication Method",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie'",
"permissions": "Permissions",
"administrator": "Administrator",
"user-can-invite-other-to-group": "User can invite other to group",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Keyword",
"link-copied": "Link Copied",
"loading": "Loading",
"loading-events": "Loading Events",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Create a New Meal Plan",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Dinner This Week",
"dinner-today": "Dinner Today",
"dinner-tonight": "DINNER TONIGHT",
@@ -470,9 +473,11 @@
"add-to-timeline": "Add to Timeline",
"recipe-added-to-list": "Recipe added to list",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recipe added to mealplan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Failed to add recipe to mealplan",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Yield",
"quantity": "Quantity",
"choose-unit": "Choose Unit",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "How did it turn out?",
"user-made-this": "{user} made this",
"last-made-date": "Last Made {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Import original keywords as tags",
"stay-in-edit-mode": "Stay in Edit mode",
"import-from-zip": "Import from Zip",
@@ -553,7 +560,8 @@
"unit": "Unit",
"upload-image": "Upload image",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Advanced Search",
@@ -787,7 +795,8 @@
"tags": "Tags",
"untagged-count": "Untagged {count}",
"create-a-tag": "Create a Tag",
"tag-name": "Tag Name"
"tag-name": "Tag Name",
"tag": "Tag"
},
"tool": {
"tools": "Tools",
@@ -796,7 +805,8 @@
"tool-name": "Tool Name",
"create-new-tool": "Create New Tool",
"on-hand-checkbox-label": "Show as On Hand (Checked)",
"required-tools": "Required Tools"
"required-tools": "Required Tools",
"tool": "Tool"
},
"user": {
"admin": "Admin",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Etiqueta",
"link-copied": "Enlace copiado",
"loading": "Loading",
"loading-events": "Cargando Eventos",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Actualizar",
"upload-file": "Subir Archivo",
"created-on-date": "Creado el {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Por favor, confirma que deseas eliminar <b>{groupName}<b/>",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Crear un nuevo menú",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Cena para esta semana",
"dinner-today": "Cena para hoy",
"dinner-tonight": "Cena para esta noche",
@@ -470,9 +473,11 @@
"add-to-timeline": "Añadir al cronograma",
"recipe-added-to-list": "Receta añadida a la lista",
"recipes-added-to-list": "Recetas añadidas a la lista",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Receta añadida al menú",
"failed-to-add-recipes-to-list": "Error al añadir las recetas a la lista",
"failed-to-add-recipe-to-mealplan": "Error al añadir receta al menú",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Raciones",
"quantity": "Cantidad",
"choose-unit": "Elija unidad",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "¿Cómo resultó esto?",
"user-made-this": "{user} hizo esto",
"last-made-date": "Cocinado por última vez el {date}",
"api-extras-description": "Los extras de las recetas son una característica clave de la API de Mealie. Permiten crear pares json clave/valor personalizados dentro de una receta para acceder desde aplicaciones de terceros. Puede utilizar estas claves para almacenar información, para activar la automatización o mensajes personalizados para transmitir al dispositivo deseado.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Clave de mensaje",
"parse": "Analizar",
"attach-images-hint": "Adjuntar imágenes arrastrando y soltando en el editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "El nombre de la receta debe ser único",
"scrape-recipe": "Analiza receta",
"scrape-recipe-description": "Importa una receta por URL. Proporcione la URL para el sitio que desea importar, y Mealie intentará importar la receta de ese sitio y añadirla a su colección.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Importar palabras clave originales como etiquetas",
"stay-in-edit-mode": "Permanecer en modo edición",
"import-from-zip": "Importar desde zip",
@@ -553,7 +560,8 @@
"unit": "Unidades",
"upload-image": "Subir imagen",
"screen-awake": "Mantener la pantalla encendida",
"remove-image": "Eliminar imagen"
"remove-image": "Eliminar imagen",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Búsqueda avanzada",
@@ -787,7 +795,8 @@
"tags": "Etiquetas",
"untagged-count": "{count} sin etiquetar",
"create-a-tag": "Crear una etiqueta",
"tag-name": "Nombre de la etiqueta"
"tag-name": "Nombre de la etiqueta",
"tag": "Etiqueta"
},
"tool": {
"tools": "Utensilios",
@@ -796,7 +805,8 @@
"tool-name": "Nombre del utensilio",
"create-new-tool": "Crear nuevo utensilio",
"on-hand-checkbox-label": "Mostrar como disponible (marcado)",
"required-tools": "Utensilios necesarios"
"required-tools": "Utensilios necesarios",
"tool": "Herramienta"
},
"user": {
"admin": "Administrador/a",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Hakusana",
"link-copied": "Linkki kopioitu",
"loading": "Loading",
"loading-events": "Ladataan tapahtumia",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Päivitä",
"upload-file": "Tuo tiedosto",
"created-on-date": "Luotu {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Haluatko varmasti poistaa ryhmän <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Luo uusi ateriasuunnitelma",
"update-this-meal-plan": "Päivitä tämä ateriasuunnitelma",
"dinner-this-week": "Viikon päivällinen",
"dinner-today": "Päivällinen tänään",
"dinner-tonight": "PÄIVÄLLINEN TÄNÄÄN",
@@ -470,9 +473,11 @@
"add-to-timeline": "Lisää aikajanalle",
"recipe-added-to-list": "Resepti lisätty listalle",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Resepti lisätty ateriasuunnitelmaan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Reseptiä ei voitu lisätä ateriasuunnitelmaan",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Sato",
"quantity": "Määrä",
"choose-unit": "Valitse Yksikkö",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Miten se onnistui?",
"user-made-this": "{user} teki tämän",
"last-made-date": "Viimeksi valmistettu {date}",
"api-extras-description": "Reseptilisät ovat Mealie API: n avainominaisuus. Niiden avulla voit luoda mukautettuja json-avain/arvo-pareja reseptin sisällä viitaten kolmannen osapuolen sovelluksiin. Voit käyttää näitä pareja sisältämään tietoja automaation käynnistämiseksi tai mukautettuja viestejä välitettäväksi haluamaasi laitteeseen.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Viestiavain",
"parse": "Jäsennä",
"attach-images-hint": "Liitä kuvia vetämällä ja pudottamalla ne editoriin",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Reseptin nimen on oltava yksilöllinen",
"scrape-recipe": "Reseptin kaappain",
"scrape-recipe-description": "Kaappaa resepti urlin avulla. Anna sen reseptin url-osoite, jonka haluat kaapata, ja Mealie yrittää kaapata reseptin kyseiseltä sivustolta ja lisätä sen kokoelmaasi.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Tuo alkuperäiset avainsanat tunnisteiksi",
"stay-in-edit-mode": "Pysy muokkaustilassa",
"import-from-zip": "Tuo zip-arkistosta",
@@ -553,7 +560,8 @@
"unit": "Yksikkö",
"upload-image": "Lataa kuva",
"screen-awake": "Pidä näyttö aina päällä",
"remove-image": "Poista kuva"
"remove-image": "Poista kuva",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Tarkennettu haku",
@@ -787,7 +795,8 @@
"tags": "Tunnisteet",
"untagged-count": "Tunnisteettomat {count}",
"create-a-tag": "Luo tunniste",
"tag-name": "Tunnisteen nimi"
"tag-name": "Tunnisteen nimi",
"tag": "Tag"
},
"tool": {
"tools": "Työkalut",
@@ -796,7 +805,8 @@
"tool-name": "Työkalun Nimi",
"create-new-tool": "Luo Uusi Työkalu",
"on-hand-checkbox-label": "Näytä työkalut, jotka omistan jo (valittu)",
"required-tools": "Tarvittavat Työkalut"
"required-tools": "Tarvittavat Työkalut",
"tool": "Tool"
},
"user": {
"admin": "Ylläpitäjä",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Mot-clé",
"link-copied": "Lien copié",
"loading": "Chargement",
"loading-events": "Chargement des événements",
"loading-recipe": "Chargement de la recette...",
"loading-ocr-data": "Chargement des données OCR...",
@@ -197,7 +198,8 @@
"refresh": "Actualiser",
"upload-file": "Transférer un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications."
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous les enregistrer? Ok pour enregistrer, annuler pour ignorer les modifications.",
"clipboard-copy-failure": "Échec de la copie vers le presse-papiers."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Êtes-vous certain de vouloir supprimer <b>{groupName}<b/>?",
@@ -248,7 +250,7 @@
"general-preferences": "Préférences générales",
"group-recipe-preferences": "Préférences de recette du groupe",
"report": "Rapport",
"report-with-id": "Identifiant du rapport : {id}",
"report-with-id": "ID du rapport: {id}",
"group-management": "Gestion des groupes",
"admin-group-management": "Administration des groupes",
"admin-group-management-text": "Les modifications apportées à ce groupe seront immédiatement prises en compte.",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Créer un nouveau menu",
"update-this-meal-plan": "Mettre à jour ce menu",
"dinner-this-week": "Menu de la semaine",
"dinner-today": "Menu du jour",
"dinner-tonight": "AU MENU CE SOIR",
@@ -470,9 +473,11 @@
"add-to-timeline": "Ajouter à lhistorique",
"recipe-added-to-list": "Recette ajoutée à la liste",
"recipes-added-to-list": "Recettes ajoutées à la liste",
"successfully-added-to-list": "Ajouté à la liste",
"recipe-added-to-mealplan": "Recette ajoutée à la planification des repas",
"failed-to-add-recipes-to-list": "Impossible dajouter la recette à la liste",
"failed-to-add-recipe-to-mealplan": "Échec de l'ajout de la recette à la planification des repas",
"failed-to-add-to-list": "Ajout dans la liste en échec",
"yield": "Rendement",
"quantity": "Quantité",
"choose-unit": "Choisir une unité",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Cétait bon?",
"user-made-this": "{user} la cuisiné",
"last-made-date": "Cuisiné le {date}",
"api-extras-description": "Les extras de recette sont une caractéristique clé de l'API Mealie. Ils vous permettent de créer des paires clé/valeur json personnalisées dans une recette à référencer depuis des applications externes. Vous pouvez utiliser ces clés pour stocker des informations utiles à l'automatisation ou des messages personnalisés à relayer vers l'appareil souhaité.",
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de lAPI Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à lappareil souhaité.",
"message-key": "Clé de message",
"parse": "Analyser",
"attach-images-hint": "Ajouter des images en les glissant-déposant dans l'éditeur",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Les noms de nouvelles recettes doivent être uniques",
"scrape-recipe": "Récupérer une recette",
"scrape-recipe-description": "Récupérer une recette par URL. Fournissez l'URL de la page que vous voulez récupérer, et Mealie essaiera d'en extraire la recette pour l'ajouter à votre collection.",
"scrape-recipe-have-a-lot-of-recipes": "Vous avez un tas de recettes à récupérer dun coup ?",
"scrape-recipe-suggest-bulk-importer": "Essayez limportateur de masse",
"import-original-keywords-as-tags": "Importer les mots-clés d'origine en tant que tags",
"stay-in-edit-mode": "Rester en mode édition",
"import-from-zip": "Importer depuis un zip",
@@ -551,9 +558,10 @@
"tree-view": "Vue en arborescence",
"recipe-yield": "Nombre de parts",
"unit": "Unité",
"upload-image": "Envoyer une image",
"upload-image": "Ajouter une image",
"screen-awake": "Garder lécran allumé",
"remove-image": "Supprimer limage"
"remove-image": "Supprimer limage",
"nextStep": "Étape suivante"
},
"search": {
"advanced-search": "Recherche avancée",
@@ -572,7 +580,7 @@
"search-hint": "Appuyez sur « /»",
"advanced": "Avancé",
"auto-search": "Recherche automatique",
"no-results": "Aucun résultat trouvé"
"no-results": "Pas de résultats trouvés"
},
"settings": {
"add-a-new-theme": "Ajouter un nouveau thème",
@@ -787,7 +795,8 @@
"tags": "Mots-clés",
"untagged-count": "{count} sans mot-clés",
"create-a-tag": "Créer une étiquette",
"tag-name": "Nom d'étiquette"
"tag-name": "Nom d'étiquette",
"tag": "Étiquette"
},
"tool": {
"tools": "Outils",
@@ -796,7 +805,8 @@
"tool-name": "Nom de lustensile",
"create-new-tool": "Créer un nouvel ustensile",
"on-hand-checkbox-label": "Afficher comme disponible (coché)",
"required-tools": "Ustensiles nécessaires"
"required-tools": "Ustensiles nécessaires",
"tool": "Ustensile"
},
"user": {
"admin": "Administrateur",
@@ -875,7 +885,7 @@
"user-management": "Gestion des utilisateurs",
"reset-locked-users": "Réinitialiser les utilisateurs verrouillés",
"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-name": "Nom d'utilisateur",
"authentication-method": "Méthode d'authentification",

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Mot-clé",
"link-copied": "Lien copié",
"loading": "Chargement",
"loading-events": "Chargement des événements",
"loading-recipe": "Chargement de la recette...",
"loading-ocr-data": "Chargement des données OCR...",
@@ -197,7 +198,8 @@
"refresh": "Actualiser",
"upload-file": "Transférer un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications."
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications.",
"clipboard-copy-failure": "Échec de la copie dans le presse-papiers."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Voulez-vous vraiment supprimer <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Créer un nouveau menu",
"update-this-meal-plan": "Mettre à jour ce menu",
"dinner-this-week": "Au menu cette semaine",
"dinner-today": "Menu du jour",
"dinner-tonight": "AU MENU CE SOIR",
@@ -470,9 +473,11 @@
"add-to-timeline": "Ajouter à lhistorique",
"recipe-added-to-list": "Recette ajoutée à la liste",
"recipes-added-to-list": "Recettes ajoutées à la liste",
"successfully-added-to-list": "Ajouté à la liste",
"recipe-added-to-mealplan": "Recette ajoutée au menu",
"failed-to-add-recipes-to-list": "Impossible dajouter la recette à la liste",
"failed-to-add-recipe-to-mealplan": "Échec de lajout de la recette au menu",
"failed-to-add-to-list": "Ajout dans la liste en échec",
"yield": "Nombre de portions",
"quantity": "Quantité",
"choose-unit": "Choisissez une unité",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Cétait bon?",
"user-made-this": "{user} la cuisiné",
"last-made-date": "Cuisiné le {date}",
"api-extras-description": "Les extras de recette sont une caractéristique clé de l'API Mealie. Ils vous permettent de créer des paires clé/valeur json personnalisées dans une recette à référencer depuis des applications externes. Vous pouvez utiliser ces clés pour stocker des informations utiles à l'automatisation ou des messages personnalisés à relayer vers l'appareil souhaité.",
"api-extras-description": "Les suppléments des recettes sont une fonctionnalité clé de lAPI Mealie. Ils permettent de créer des paires JSON clé/valeur personnalisées dans une recette, qui peuvent être référencées depuis des applications tierces. Ces clés peuvent être utilisées par exemple pour déclencher des tâches automatisées ou des messages personnalisés à transmettre à lappareil souhaité.",
"message-key": "Clé de message",
"parse": "Analyser",
"attach-images-hint": "Ajouter des images en les glissant-déposant dans l'éditeur",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Les noms de nouvelles recettes doivent être uniques",
"scrape-recipe": "Récupérer une recette",
"scrape-recipe-description": "Récupérer une recette par URL. Fournissez l'URL de la page que vous voulez récupérer, et Mealie essaiera d'en extraire la recette pour l'ajouter à votre collection.",
"scrape-recipe-have-a-lot-of-recipes": "Vous avez un tas de recettes à récupérer dun coup ?",
"scrape-recipe-suggest-bulk-importer": "Essayez limportateur de masse",
"import-original-keywords-as-tags": "Importer les mots-clés d'origine en tant que tags",
"stay-in-edit-mode": "Rester en mode édition",
"import-from-zip": "Importer depuis un zip",
@@ -553,7 +560,8 @@
"unit": "Unité",
"upload-image": "Envoyer une image",
"screen-awake": "Garder lécran allumé",
"remove-image": "Supprimer limage"
"remove-image": "Supprimer limage",
"nextStep": "Étape suivante"
},
"search": {
"advanced-search": "Recherche avancée",
@@ -787,7 +795,8 @@
"tags": "Mots-clés",
"untagged-count": "{count} sans mot-clés",
"create-a-tag": "Créer un mot-clé",
"tag-name": "Mot-clé"
"tag-name": "Mot-clé",
"tag": "Étiquette"
},
"tool": {
"tools": "Ustensiles",
@@ -796,7 +805,8 @@
"tool-name": "Nom de lustensile",
"create-new-tool": "Créer un nouvel ustensile",
"on-hand-checkbox-label": "Afficher comme disponible (coché)",
"required-tools": "Ustensiles nécessaires"
"required-tools": "Ustensiles nécessaires",
"tool": "Ustensile"
},
"user": {
"admin": "Administrateur",

View File

@@ -1,61 +1,61 @@
{
"about": {
"about": "About",
"about-mealie": "About Mealie",
"api-docs": "API Docs",
"api-port": "API Port",
"application-mode": "Application Mode",
"database-type": "Database Type",
"database-url": "Database URL",
"default-group": "Default Group",
"demo": "Demo",
"demo-status": "Demo Status",
"development": "Development",
"docs": "Docs",
"download-log": "Download Log",
"download-recipe-json": "Last Scraped JSON",
"github": "Github",
"log-lines": "Log Lines",
"not-demo": "Not Demo",
"portfolio": "Portfolio",
"production": "Production",
"support": "Support",
"version": "Version",
"unknown-version": "unknown",
"sponsor": "Sponsor"
"about": "Acerca de",
"about-mealie": "Acerca de Mealie",
"api-docs": "Documentación da API",
"api-port": "Porto da API",
"application-mode": "Modo da Aplicación",
"database-type": "Tipo de base de datos",
"database-url": "URL da base de datos",
"default-group": "Grupo por defecto",
"demo": "Demostración",
"demo-status": "Estado da demostración",
"development": "Desenvolvemento",
"docs": "Documentación",
"download-log": "Descargar rexistro",
"download-recipe-json": "Último JSON raspado",
"github": "GitHub",
"log-lines": "Liñas de rexistro",
"not-demo": "Non demostración",
"portfolio": "Portafolio",
"production": "Producn",
"support": "Soporte",
"version": "Versión",
"unknown-version": "descoñecido",
"sponsor": "Patrocinador"
},
"asset": {
"assets": "Assets",
"code": "Code",
"file": "File",
"image": "Image",
"new-asset": "New Asset",
"assets": "Activos",
"code": "Código",
"file": "Ficheiro",
"image": "Imaxe",
"new-asset": "Novo documento",
"pdf": "PDF",
"recipe": "Recipe",
"show-assets": "Show Assets",
"error-submitting-form": "Error Submitting Form"
"recipe": "Receita",
"show-assets": "Amosar documentos",
"error-submitting-form": "Erro ao enviar formulario"
},
"category": {
"categories": "Categories",
"category-created": "Category created",
"category-creation-failed": "Category creation failed",
"category-deleted": "Category Deleted",
"category-deletion-failed": "Category deletion failed",
"category-filter": "Category Filter",
"category-update-failed": "Category update failed",
"category-updated": "Category updated",
"uncategorized-count": "Uncategorized {count}",
"create-a-category": "Create a Category",
"category-name": "Category Name",
"category": "Category"
"categories": "Categorías",
"category-created": "Categoría creada",
"category-creation-failed": "Fallou a creación da categoría",
"category-deleted": "Categoría eliminada",
"category-deletion-failed": "Fallou a eliminación da categoría",
"category-filter": "Filtro de categoría",
"category-update-failed": "Fallou a actualización da categoría",
"category-updated": "Categoría actualizada",
"uncategorized-count": "Sen categorizar {count}",
"create-a-category": "Crear unha categoría",
"category-name": "Nome da categoría",
"category": "Categoría"
},
"events": {
"apprise-url": "Apprise URL",
"database": "Database",
"delete-event": "Delete Event",
"event-delete-confirmation": "Are you sure you want to delete this event?",
"event-deleted": "Event Deleted",
"event-updated": "Event Updated",
"apprise-url": "URL de avisos",
"database": "Base de datos",
"delete-event": "Eliminar evento",
"event-delete-confirmation": "Estás seguro de que queres eliminar este evento?",
"event-deleted": "Evento eliminado",
"event-updated": "Evento actualizado",
"new-notification-form-description": "Mealie uses the Apprise library to generate notifications. They offer many options for services to use for notifications. Refer to their wiki for a comprehensive guide on how to create the URL for your service. If available, selecting the type of your notification may include extra features.",
"new-version": "New version available!",
"notification": "Notification",
@@ -108,55 +108,56 @@
"general": "General",
"get": "Get",
"home": "Home",
"image": "Image",
"image-upload-failed": "Image upload failed",
"import": "Import",
"image": "Imaxe",
"image-upload-failed": "Fallou a subida da imaxe",
"import": "Importar",
"json": "JSON",
"keyword": "Keyword",
"link-copied": "Link Copied",
"loading-events": "Loading Events",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipes": "Loading Recipes",
"message": "Message",
"monday": "Monday",
"name": "Name",
"new": "New",
"never": "Never",
"no": "No",
"no-recipe-found": "No Recipe Found",
"keyword": "Palabra chave",
"link-copied": "Enlace copiado",
"loading": "Cargando",
"loading-events": "Cargando eventos",
"loading-recipe": "Cargando receita...",
"loading-ocr-data": "Cargando información de OCR...",
"loading-recipes": "Cargando receitas",
"message": "Mensaxe",
"monday": "Luns",
"name": "Nome",
"new": "Nova",
"never": "Nunca",
"no": "Non",
"no-recipe-found": "Non se atopou ningunha receita",
"ok": "OK",
"options": "Options:",
"plural-name": "Plural Name",
"print": "Print",
"print-preferences": "Print Preferences",
"random": "Random",
"rating": "Rating",
"recent": "Recent",
"recipe": "Recipe",
"recipes": "Recipes",
"rename-object": "Rename {0}",
"reset": "Reset",
"saturday": "Saturday",
"save": "Save",
"settings": "Settings",
"share": "Share",
"shuffle": "Shuffle",
"sort": "Sort",
"sort-alphabetically": "Alphabetical",
"status": "Status",
"subject": "Subject",
"submit": "Submit",
"success-count": "Success: {count}",
"sunday": "Sunday",
"templates": "Templates:",
"test": "Test",
"themes": "Themes",
"thursday": "Thursday",
"token": "Token",
"tuesday": "Tuesday",
"type": "Type",
"update": "Update",
"options": "Opcións:",
"plural-name": "Nome plural",
"print": "Imprimir",
"print-preferences": "Preferencias de impresión",
"random": "Ao chou",
"rating": "Puntuación",
"recent": "Recentes",
"recipe": "Receita",
"recipes": "Receitas",
"rename-object": "Renomear {0}",
"reset": "Restablecer",
"saturday": "Sábado",
"save": "Gardar",
"settings": "Axustes",
"share": "Compartir",
"shuffle": "Barallar",
"sort": "Ordenar",
"sort-alphabetically": "Alfabético",
"status": "Estado",
"subject": "Asunto",
"submit": "Enviar",
"success-count": "Éxito: {count}",
"sunday": "Domingo",
"templates": "Modelos:",
"test": "Probar",
"themes": "Temas",
"thursday": "Xoves",
"token": "Identificador",
"tuesday": "Martes",
"type": "Tipo",
"update": "Actualizar",
"updated": "Updated",
"upload": "Upload",
"url": "URL",
@@ -197,7 +198,8 @@
"refresh": "Refresh",
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Are you sure you want to delete <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Create a New Meal Plan",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Dinner This Week",
"dinner-today": "Dinner Today",
"dinner-tonight": "DINNER TONIGHT",
@@ -470,9 +473,11 @@
"add-to-timeline": "Add to Timeline",
"recipe-added-to-list": "Recipe added to list",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recipe added to mealplan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Failed to add recipe to mealplan",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Yield",
"quantity": "Quantity",
"choose-unit": "Choose Unit",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "How did it turn out?",
"user-made-this": "{user} made this",
"last-made-date": "Last Made {date}",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Message Key",
"parse": "Parse",
"attach-images-hint": "Attach images by dragging & dropping them into the editor",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "New recipe names must be unique",
"scrape-recipe": "Scrape Recipe",
"scrape-recipe-description": "Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the recipe from that site and add it to your collection.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Import original keywords as tags",
"stay-in-edit-mode": "Stay in Edit mode",
"import-from-zip": "Import from Zip",
@@ -553,7 +560,8 @@
"unit": "Unit",
"upload-image": "Upload image",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Advanced Search",
@@ -787,7 +795,8 @@
"tags": "Tags",
"untagged-count": "Untagged {count}",
"create-a-tag": "Create a Tag",
"tag-name": "Tag Name"
"tag-name": "Tag Name",
"tag": "Etiqueta"
},
"tool": {
"tools": "Tools",
@@ -796,7 +805,8 @@
"tool-name": "Tool Name",
"create-new-tool": "Create New Tool",
"on-hand-checkbox-label": "Show as On Hand (Checked)",
"required-tools": "Required Tools"
"required-tools": "Required Tools",
"tool": "Ferramenta"
},
"user": {
"admin": "Admin",

View File

@@ -77,7 +77,7 @@
"tag-events": "אירועי תגיות",
"category-events": "אירועי קטגוריות",
"when-a-new-user-joins-your-group": "כאשר משתמש חדש מצטרף לקבוצה",
"recipe-events": "Recipe Events"
"recipe-events": "אירועי מתכון"
},
"general": {
"cancel": "ביטול",
@@ -114,9 +114,10 @@
"json": "JSON",
"keyword": "מילת מפתח",
"link-copied": "קישור הועתק",
"loading": "טעינה",
"loading-events": "טוען",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "טוען מתכון...",
"loading-ocr-data": "טוען נתוני OCR...",
"loading-recipes": "מתכונים בטעינה",
"message": "הודעה",
"monday": "שני",
@@ -127,7 +128,7 @@
"no-recipe-found": "לא נמצא מתכון",
"ok": "אישור",
"options": "אפשרויות:",
"plural-name": "Plural Name",
"plural-name": "שם ברבים",
"print": "הדפסה",
"print-preferences": "העדפות הדפסה",
"random": "אקראי",
@@ -197,7 +198,8 @@
"refresh": "רענן",
"upload-file": "העלאת קבצים",
"created-on-date": "נוצר ב-{0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "יש שינויים שלא נשמרו. לצאת לפני שמירה? אשר לשמירה, בטל למחיקת שינויים.",
"clipboard-copy-failure": "כשלון בהעתקה ללוח ההדבקה."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "האם את/ה בטוח/ה שברצונך למחוק את <b>{groupName}<b/>?",
@@ -212,7 +214,7 @@
"group-id-with-value": "מזהה קבוצה: {groupID}",
"group-name": "שם קבוצה",
"group-not-found": "קבוצה לא נמצאה",
"group-token": "Group Token",
"group-token": "טוקן קבוצתי",
"group-with-value": "קבוצה: {groupID}",
"groups": "קבוצות",
"manage-groups": "ניהול קבוצות",
@@ -248,7 +250,7 @@
"general-preferences": "העדפות כלליות",
"group-recipe-preferences": "העדפות קבוצה",
"report": "דיווח",
"report-with-id": "Report ID: {id}",
"report-with-id": "מספר דוח: {id}",
"group-management": "ניהול קבוצה",
"admin-group-management": "ניהול קבוצת מנהל",
"admin-group-management-text": "השינויים לקבוצה זו יתבצעו מיידית.",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "יצירת תכנית ארוחות חדשה",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "ארוחות ערב השבוע",
"dinner-today": "ארוחת ערב היום",
"dinner-tonight": "ארוחת ערב היום",
@@ -469,10 +472,12 @@
"add-to-plan": "הוספה לתכנית",
"add-to-timeline": "הוסף לציר הזמן",
"recipe-added-to-list": "מתכון נוסף לרשימה",
"recipes-added-to-list": "Recipes added to list",
"recipes-added-to-list": "מתכונים הוספו לרשימה",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "מתכון נוסף לתכנון ארוחות",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipes-to-list": "כשלון בהוספת מתכון לרשימה",
"failed-to-add-recipe-to-mealplan": "הוספת מתכון לתכנון ארוחות נכשלה",
"failed-to-add-to-list": "Failed to add to list",
"yield": "תשואה",
"quantity": "כמות",
"choose-unit": "בחירת יחידת מידה",
@@ -509,11 +514,11 @@
"how-did-it-turn-out": "איך יצא?",
"user-made-this": "{user} הכין את זה",
"last-made-date": "נעשה לאחרונה ב{date}",
"api-extras-description": "מתכונים אקסטרה הם פונקציה שמאפשרת שימוש ב- Mealie API. צמדי json של key/value במתכון מאפשרים לצד שלישי לקרוא להם. ניתן להשתמש במפתחות להכיל מידע על מנת להפעיל אוטומציות או הודעות מותאמות אישית למכשירים הרלוונטיים.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "מפתח הודעה",
"parse": "ניתוח",
"attach-images-hint": "הוסף תמונות ע\"י גרירה ושחרור אל תוך העורך",
"drop-image": "Drop image",
"drop-image": "גרור תמונה",
"enable-ingredient-amounts-to-use-this-feature": "אפשר לכמות המרכיבים להשתמש בפונקציה",
"recipes-with-units-or-foods-defined-cannot-be-parsed": "לא ניתן לפענך מתכונים בהם יחידות או אוכל מוגדרים.",
"parse-ingredients": "חילוץ רכיבים",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "שם מתכון חדש חייב להיות ייחודי",
"scrape-recipe": "קריאת מתכון",
"scrape-recipe-description": "קריאת מתכון בעזרת לינק. ספק את הלינק של האתר שברצונך לקרוא, ומילי תנסה לקרוא את המתכון מהאתר ולהוסיף אותו לאוסף.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "ייבא שמות מפתח מקוריות כתגיות",
"stay-in-edit-mode": "השאר במצב עריכה",
"import-from-zip": "ייבא מקובץ",
@@ -553,7 +560,8 @@
"unit": "יחידה",
"upload-image": "העלה תמונה",
"screen-awake": "השאר את המסך פעיל",
"remove-image": "האם למחוק את התמונה?"
"remove-image": "האם למחוק את התמונה?",
"nextStep": "Next step"
},
"search": {
"advanced-search": "חיפוש מתקדם",
@@ -572,16 +580,16 @@
"search-hint": "לחץ '/'",
"advanced": "מתקדם",
"auto-search": "חיפוש אוטומטי",
"no-results": "No results found"
"no-results": "לא נמצאו תוצאות"
},
"settings": {
"add-a-new-theme": "הוסף ערכת נושא חדשה",
"admin-settings": "הגדרות מנהל",
"backup": {
"backup-created": "Backup created successfully",
"backup-created": "הגיבוי בוצע בהצלחה",
"backup-created-at-response-export_path": "גיבוי נוצר ב {path}",
"backup-deleted": "גיבוי נמחק",
"restore-success": "Restore successful",
"restore-success": "השחזור הצליח",
"backup-tag": "תגית גיבוי",
"create-heading": "Create a Backup",
"delete-backup": "מחיקת גיבוי",
@@ -690,13 +698,13 @@
"configuration": "הגדרות",
"docker-volume": "Docker Volume",
"docker-volume-help": "מילי דורשת שקונטיינר הקצה הקדמי והקצה האחורי חולקים את באותו כונן / שטח אחסון בדוקר.\nכל זאת כל מנת לוודא שלקונטיינר תהיה גישה לתמונות הגיבוי או לנכסים בדיסק.",
"volumes-are-misconfigured": "Volumes are misconfigured.",
"volumes-are-misconfigured": "כוננים אינם מוגדרים בצורה תקינה.",
"volumes-are-configured-correctly": "ה-Volumeים מוגדרים תקין.",
"status-unknown-try-running-a-validation": "מצב לא ידוע. נסה להריץ אימות.",
"validate": "אימות",
"email-configuration-status": "מצב הגדרות דוא״ל",
"email-configured": "Email Configured",
"email-test-results": "Email Test Results",
"email-configured": "דואר אלקטרוני הוגדר",
"email-test-results": "תוצאות בדיקת דואר אלקטרוני",
"ready": "מוכן",
"not-ready": "לא מוכן - בדוק משתני סביבה",
"succeeded": "הצליח",
@@ -787,7 +795,8 @@
"tags": "תגיות",
"untagged-count": "לא מתוייג {count}",
"create-a-tag": "צור תגית",
"tag-name": "שם תגית"
"tag-name": "שם תגית",
"tag": "Tag"
},
"tool": {
"tools": "כלים",
@@ -796,7 +805,8 @@
"tool-name": "שם כלי",
"create-new-tool": "יצירת כלי חדש",
"on-hand-checkbox-label": "הראה מה יש לי במטבח",
"required-tools": "צריך כלים"
"required-tools": "צריך כלים",
"tool": "Tool"
},
"user": {
"admin": "אדמין",
@@ -831,7 +841,7 @@
"password-updated": "הסיסמה עודכנה",
"password": "סיסמה",
"password-strength": "חוזק הסיסמה {strength}",
"please-enter-password": "Please enter your new password.",
"please-enter-password": "אנא הזן סיסמה חדשה.",
"register": "הרשמה",
"reset-password": "איפוס סיסמה",
"sign-in": "התחברות",
@@ -852,7 +862,7 @@
"username": "שם משתמש",
"users-header": "משתמשים",
"users": "משתמשים",
"user-not-found": "User not found",
"user-not-found": "משתמש לא נמצא",
"webhook-time": "זמן Webhook",
"webhooks-enabled": "הפעלת Webhooks",
"you-are-not-allowed-to-create-a-user": "אין הרשאה ליצור משתמש",
@@ -875,7 +885,7 @@
"user-management": "ניהול משתמשים",
"reset-locked-users": "אתחל משתמשים נעולים",
"admin-user-creation": "יצירת משתמש אדמין",
"admin-user-management": "Admin User Management",
"admin-user-management": "ניהול משתמשים",
"user-details": "פרטי משתמש",
"user-name": "שם משתמש",
"authentication-method": "שיטת אימות",
@@ -886,9 +896,9 @@
"user-can-manage-group": "משתמש יכול לנהל קבוצה",
"user-can-organize-group-data": "משתמש יכול לשנות מידע של קבוצה",
"enable-advanced-features": "אפשר אפשרויות מתקדמות",
"it-looks-like-this-is-your-first-time-logging-in": "It looks like this is your first time logging in.",
"it-looks-like-this-is-your-first-time-logging-in": "נראה שזו ההתחברות הראשונה שלך.",
"dont-want-to-see-this-anymore-be-sure-to-change-your-email": "Don't want to see this anymore? Be sure to change your email in your user settings!",
"forgot-password": "Forgot Password",
"forgot-password": "שכחתי סיסמא",
"forgot-password-text": "Please enter your email address and we will send you a link to reset your password.",
"changes-reflected-immediately": "Changes to this user will be reflected immediately."
},
@@ -964,8 +974,8 @@
"delete-recipes": "מחיקת מתכונים",
"source-unit-will-be-deleted": "יחידת המקור תמחק"
},
"create-alias": "Create Alias",
"manage-aliases": "Manage Aliases",
"create-alias": "יצירת שם נרדף",
"manage-aliases": "נהל שמות נרדפים",
"seed-data": "אכלס נתונים",
"seed": "אכלס",
"data-management": "ניהול מידע",
@@ -975,24 +985,24 @@
"columns": "עמודות",
"combine": "שילוב",
"categories": {
"edit-category": "Edit Category",
"new-category": "New Category",
"category-data": "Category Data"
"edit-category": "ערוך קטגוריה",
"new-category": "קטגוריה חדשה",
"category-data": "נתוני קטגוריה"
},
"tags": {
"new-tag": "New Tag",
"edit-tag": "Edit Tag",
"tag-data": "Tag Data"
"new-tag": "טאג חדש",
"edit-tag": "ערוך טאג",
"tag-data": "נתוני טאגים"
},
"tools": {
"new-tool": "New Tool",
"edit-tool": "Edit Tool",
"tool-data": "Tool Data"
"new-tool": "כלי חדש",
"edit-tool": "ערוך כלי",
"tool-data": "נתוני כלים"
}
},
"user-registration": {
"user-registration": "רישום משתמשים",
"registration-success": "Registration Success",
"registration-success": "ההרשמה הושלמה",
"join-a-group": "הצטרפות לקבוצה",
"create-a-new-group": "יצירת קבוצה חדשה",
"provide-registration-token-description": "ספק בבקשה את טוקן הרישום המשוייך לקבוצה אליה ברצונך להצטרף. בכדי לקבל אותו יהיה צורך להשיג אותו מאחד מחברה הקבוצה הקיימים.",
@@ -1039,7 +1049,7 @@
},
"ocr-editor": {
"ocr-editor": "עורך סריקת תווים",
"toolbar": "Toolbar",
"toolbar": "סרגל כלים",
"selection-mode": "מצב בחירה",
"pan-and-zoom-picture": "סובב והגדל תמונה",
"split-text": "פיצול טקסט",
@@ -1047,8 +1057,8 @@
"split-by-block": "פצל לפי מבנה המלל",
"flatten": "שטח בלי קשר למבנה המקורי",
"help": {
"help": "Help",
"mouse-modes": "Mouse modes",
"help": "עזרה",
"mouse-modes": "מצבי עכבר",
"selection-mode": "מצב בחירה (ברירת מחדל)",
"selection-mode-desc": "מצב בחירה זה הינו הראשי אשר ישמש להכנסת מידע:",
"selection-mode-steps": {

View File

@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Ključna riječ",
"link-copied": "Poveznica kopirana",
"loading": "Loading",
"loading-events": "Učitavanje događaja",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
@@ -197,7 +198,8 @@
"refresh": "Osvježi",
"upload-file": "Prenesi Datoteku",
"created-on-date": "Kreirano dana: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes."
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Jeste li sigurni da želite izbrisati <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Kreirajte Novi Plan Obroka",
"update-this-meal-plan": "Update this Meal Plan",
"dinner-this-week": "Večera Ove Sedmice",
"dinner-today": "Večera Danas",
"dinner-tonight": "VEČERA NOĆAS",
@@ -470,9 +473,11 @@
"add-to-timeline": "Add to Timeline",
"recipe-added-to-list": "Recept je dodan na popis",
"recipes-added-to-list": "Recipes added to list",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recept je dodan u Plan",
"failed-to-add-recipes-to-list": "Failed to add recipe to list",
"failed-to-add-recipe-to-mealplan": "Nije uspjelo dodavanje recepta u plan obroka",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Konačna Količina",
"quantity": "Količina",
"choose-unit": "Odaberi Jedinicu",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Kako je ispalo?",
"user-made-this": "{user} je napravio/la ovo",
"last-made-date": "Zadnji put napravljeno {date}",
"api-extras-description": "Dodatci recepata su ključna značajka Mealie API-ja. Omogućuju vam stvaranje prilagođenih JSON ključ/vrijednost parova unutar recepta kako biste ih mogli koristiti iz aplikacija trećih strana. Možete koristiti ove ključeve kako biste sadržavali informacije koje će pokrenuti automatizaciju ili prilagođene poruke koje će biti prenesene na željeni uređaj.",
"api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.",
"message-key": "Ključ poruke",
"parse": "Razluči (parsiraj)",
"attach-images-hint": "Priložite slike povlačenjem i ispuštanjem u uređivaču",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Naziv novog recepta mora imati jedinstveno ime",
"scrape-recipe": "Prikupi (skraperaj) recept",
"scrape-recipe-description": "Prikupi (skraperaj) recept putem URL-a. Priložite URL web stranice s koje želite prikupiti recept, a Mealie će pokušati prikupiti recept s te stranice i dodati ga u vašu kolekciju.",
"scrape-recipe-have-a-lot-of-recipes": "Have a lot of recipes you want to scrape at once?",
"scrape-recipe-suggest-bulk-importer": "Try out the bulk importer",
"import-original-keywords-as-tags": "Uvezi originalne ključne riječi kao oznake",
"stay-in-edit-mode": "Ostanite u načinu uređivanja",
"import-from-zip": "Uvoz iz Zip-a",
@@ -553,7 +560,8 @@
"unit": "Jedinica",
"upload-image": "Učitavanje Slike",
"screen-awake": "Keep Screen Awake",
"remove-image": "Remove image"
"remove-image": "Remove image",
"nextStep": "Next step"
},
"search": {
"advanced-search": "Napredno Pretraživanje",
@@ -787,7 +795,8 @@
"tags": "Oznake",
"untagged-count": "Neoznačeno {count}",
"create-a-tag": "Kreiraj Oznaku",
"tag-name": "Naziv Oznake"
"tag-name": "Naziv Oznake",
"tag": "Tag"
},
"tool": {
"tools": "Alati",
@@ -796,7 +805,8 @@
"tool-name": "Naziv Alata",
"create-new-tool": "Kreiraj Novi Alat",
"on-hand-checkbox-label": "Prikaži Alat Dostupnim (Označeno)",
"required-tools": "Potrebni Alati"
"required-tools": "Potrebni Alati",
"tool": "Tool"
},
"user": {
"admin": "Administrator",

View File

@@ -42,7 +42,7 @@
"category-deleted": "Kategória törölve",
"category-deletion-failed": "Kategória törlése sikertelen",
"category-filter": "Kategória szűrő",
"category-update-failed": "Kategória elmentése sikertelen",
"category-update-failed": "Kategória frissítése sikertelen",
"category-updated": "Kategória frissítve",
"uncategorized-count": "Kategórizálatlan {count}",
"create-a-category": "Kategória létrehozása",
@@ -53,7 +53,7 @@
"apprise-url": "Apprise URL",
"database": "Adatbázis",
"delete-event": "Esemény törlése",
"event-delete-confirmation": "Biztosan törölni akarod az eseményt?",
"event-delete-confirmation": "Biztosan törölni szeretné ezt az eseményt?",
"event-deleted": "Esemény törölve",
"event-updated": "Esemény Frissítve",
"new-notification-form-description": "A Mealie az Apprise könyvtárat használja az értesítésekhez. Számos lehetőséget kínál különbőző értesítési szolgáltatásokhoz. Nézd meg a wiki oldalukon, hogy kell URL-t létrehozni az általad használt szolgáltatáshoz. Az értesítés tipusának kiválasztásával egyéb beállítási lehetőségek jelenhetnek meg.",
@@ -77,7 +77,7 @@
"tag-events": "Címke események",
"category-events": "Kategória események",
"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": {
"cancel": "Mégsem",
@@ -114,6 +114,7 @@
"json": "JSON",
"keyword": "Kulcsszó",
"link-copied": "Hivatkozás másolva",
"loading": "Betöltés",
"loading-events": "Események betöltése",
"loading-recipe": "Recept betöltése...",
"loading-ocr-data": "OCR adatok betöltése...",
@@ -197,7 +198,8 @@
"refresh": "Frissít",
"upload-file": "Fájl feltöltése",
"created-on-date": "Létrehozva: {0}",
"unsaved-changes": "El nem mentett módosításai vannak. Szeretné elmenteni, mielőtt kilép? A mentéshez kattintson az Ok, a módosítások elvetéséhez a Mégsem gombra."
"unsaved-changes": "El nem mentett módosításai vannak. Szeretné elmenteni, mielőtt kilép? A mentéshez kattintson az Ok, a módosítások elvetéséhez a Mégsem gombra.",
"clipboard-copy-failure": "Nem sikerült a vágólapra másolás."
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Biztosan törölni szeretnéd ezt: <b>{groupName}<b/>?",
@@ -256,6 +258,7 @@
},
"meal-plan": {
"create-a-new-meal-plan": "Menüterv létrehozása",
"update-this-meal-plan": "Frissítsd ezt a Menütervet",
"dinner-this-week": "Vacsora ezen a héten",
"dinner-today": "Vacsora ma",
"dinner-tonight": "Vacsora ma",
@@ -470,9 +473,11 @@
"add-to-timeline": "Hozzáadás idővonalhoz",
"recipe-added-to-list": "Recept hozzáadva listához",
"recipes-added-to-list": "Recept hozzáadva listához",
"successfully-added-to-list": "Successfully added to list",
"recipe-added-to-mealplan": "Recept hozzáadva menütervhez",
"failed-to-add-recipes-to-list": "Nem sikerült hozzáadni a receptet a listához",
"failed-to-add-recipe-to-mealplan": "Nem sikerült hozzáadni a receptet a menütervhez",
"failed-to-add-to-list": "Failed to add to list",
"yield": "Adag",
"quantity": "Mennyiség",
"choose-unit": "Válasszon mennyiségi egységet",
@@ -509,7 +514,7 @@
"how-did-it-turn-out": "Hogyan sikerült?",
"user-made-this": "ezt {user} készítette el",
"last-made-date": "Utoljára elkészítve {date}",
"api-extras-description": "A receptek extrák a Mealie API egyik legfontosabb jellemzője. Lehetővé teszik, hogy egyéni json kulcs/érték párokat hozzon létre egy receptben, amelyekre 3. féltől származó alkalmazásokból hivatkozhat. Ezeket a kulcsokat arra használhatja, hogy automatizmusokat indítsanak el vagy küldjenek egyéni üzenetek a meghatározott eszközéhez.",
"api-extras-description": "A receptek extrái a Mealie API egyik legfontosabb szolgáltatása. Lehetővé teszik, hogy egyéni JSON kulcs/érték párokat hozzon létre egy receptben, amelyekre harmadik féltől származó alkalmazásokból hivatkozhat. Ezeket a kulcsokat információszolgáltatásra használhatja, például automatizmusok vagy egyéni üzenetek indítására, amelyeket a kívánt eszközre küldhet.",
"message-key": "Üzenetkulcs",
"parse": "Előkészítés",
"attach-images-hint": "Képek csatolása a szerkesztőbe történő húzásával és ejtésével",
@@ -531,6 +536,8 @@
"new-recipe-names-must-be-unique": "Az új recept nevének egyedinek kell lennie",
"scrape-recipe": "Recept kinyerése",
"scrape-recipe-description": "Recept (adatok) kinyerése Url alapján. Adja meg a beolvasni kívánt oldal Url-címét, és Mealie megpróbálja beolvasni a receptet az adott oldalról, majd hozzáadja azt gyűjteményéhez.",
"scrape-recipe-have-a-lot-of-recipes": "Sok receptje van, amit egyszerre szeretne átvenni?",
"scrape-recipe-suggest-bulk-importer": "Próbálja ki a tömeges importálót",
"import-original-keywords-as-tags": "Eredeti kulcsszavak importálása címkeként",
"stay-in-edit-mode": "Maradjon Szerkesztés módban",
"import-from-zip": "Importálás ZIP-ből",
@@ -553,7 +560,8 @@
"unit": "Mennyiségi egység",
"upload-image": "Kép feltöltése",
"screen-awake": "Képernyő ébren tartása",
"remove-image": "Kép etávolítása"
"remove-image": "Kép etávolítása",
"nextStep": "Következő lépés"
},
"search": {
"advanced-search": "Részletes keresés",
@@ -590,7 +598,7 @@
"import-summary": "Import összefoglaló",
"partial-backup": "Részleges biztonsági mentés",
"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",
"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.",
@@ -787,7 +795,8 @@
"tags": "Címkék",
"untagged-count": "Címkézetlen {count}",
"create-a-tag": "Címke létrehozása",
"tag-name": "Címke Neve"
"tag-name": "Címke Neve",
"tag": "Címke"
},
"tool": {
"tools": "Eszközök",
@@ -796,7 +805,8 @@
"tool-name": "Eszköz neve",
"create-new-tool": "Új eszköz létrehozása",
"on-hand-checkbox-label": "Készleten lévőnek mutatni (Ellenőrizve)",
"required-tools": "Szükséges eszközök"
"required-tools": "Szükséges eszközök",
"tool": "Eszköz"
},
"user": {
"admin": "Adminisztrátor",
@@ -1056,8 +1066,8 @@
"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."
},
"pan-and-zoom-mode": "Pan and Zoom Mode",
"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-mode": "Pásztázás és nagyítás mód",
"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-modes": {
"line-mode": "Vonal mód (alapértelmezett)",
@@ -1112,8 +1122,8 @@
"brute": "Brute",
"show-individual-confidence": "",
"ingredient-text": "Hozzávaló szöveg",
"average-confident": "{0} Confident",
"try-an-example": "Próbáld ki",
"average-confident": "{0}-os bizonyosság",
"try-an-example": "Próbáljon ki egy példát",
"parser": "Szintaxis elemző",
"background-tasks": "Háttér folyamatok",
"background-tasks-description": "Itt megtekintheti az összes futó háttérfeladatot és azok állapotát",

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More