Compare commits

...

95 Commits

Author SHA1 Message Date
mealie-commit-bot[bot]
8b9149a1ce chore: bump version to v3.18.0 2026-05-20 14:26:21 +00:00
Hayden
c8ff75c02a chore(l10n): New Crowdin updates (#7617)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-20 04:06:39 +00:00
d 🔹
3d6ff52358 fix: return HTTP 400 for duplicate tag and label creation (#7638)
Co-authored-by: voidborne-d <voidborne-d@users.noreply.github.com>
2026-05-17 15:50:32 +00:00
mealie-actions[bot]
f04b0c741c chore(l10n): Crowdin locale sync (#7637)
Co-authored-by: GitHub Action <action@github.com>
2026-05-17 03:11:13 +00:00
Hayden
742b498c1d fix: enforce ownership check on recipe deletion (GHSA-x5v9-9jvh-7c7q) (#7625) 2026-05-14 17:04:04 +00:00
Hayden
eddb0c30e0 fix: block scriptable asset extensions and force Content-Disposition: attachment (GHSA-gfwc-pjx4-mg9p) (#7626) 2026-05-14 16:08:16 +00:00
Hayden
1cebfd56ab fix: use locale for Recipe Created timeline event (#4497) (#7623) 2026-05-14 14:07:15 +00:00
Hayden
074ec7aab2 fix: downgrade OIDC missing-claims log from ERROR to DEBUG (#6801) (#7620) 2026-05-14 02:35:11 +00:00
Michael Genson
af75c5f39d fix: Infinite API request loop on empty stores (#7613) 2026-05-12 12:25:48 -05:00
Zdenek Stursa
703db2931f fix: prevent double-scaling of sub-recipe ingredients in shopping list (#7537)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 15:29:30 +00:00
Hayden
52399547d6 chore: update SECURITY.md for GitHub private vulnerability reporting (#7612) 2026-05-12 01:54:52 +00:00
Hayden
be4ff86c57 chore(l10n): New Crowdin updates (#7608)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-11 20:52:46 +00:00
Michael Genson
8a054b1be8 feat: Remember screen lock preference (#7609) 2026-05-11 14:12:50 -05:00
Zdenek Stursa
2dbfc7f72b fix: redirect to new slug URL after recipe rename (#7522)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:13:16 +00:00
Hayden
e492da67e2 chore(l10n): New Crowdin updates (#7605)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-11 13:40:52 +00:00
renovate[bot]
811be08996 fix(deps): update dependency authlib to v1.7.2 (#7606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-11 10:01:42 +00:00
Michael Genson
fdd17182d8 fix: Update OpenAI recipe parse prompt to return the same number of ingredients as given (#7604) 2026-05-10 22:24:47 -05:00
Michael Genson
d340fdd9df fix: Update backend normalization to match search normalization logic (#7603)
Co-authored-by: Copilot <copilot@github.com>
2026-05-10 21:23:57 -05:00
Zdenek Stursa
551a92a031 fix: redirect to login and validate input on password reset flow (#7521)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Michael Genson <genson.michael@gmail.com>
2026-05-10 13:37:46 -05:00
Zdenek Stursa
8c06f49b02 fix: make PWA share target functional on Android Chrome (#7468)
Co-authored-by: Zdenek <tvuj-email@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:21:16 +00:00
Michael Genson
9fd3fbca8b feat: Improve new shopping list UI (#7600)
Co-authored-by: Copilot <copilot@github.com>
2026-05-10 13:15:20 -05:00
Hayden
a242aea9f2 chore(l10n): New Crowdin updates (#7589)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-10 17:02:45 +00:00
Michael Genson
6e9ad5fef1 fix: Query Filter Builder "Advanced" bug (#7599)
Co-authored-by: Copilot <copilot@github.com>
2026-05-10 11:51:27 -05:00
mealie-actions[bot]
ee181a598b chore(l10n): Crowdin locale sync (#7595)
Co-authored-by: GitHub Action <action@github.com>
2026-05-10 03:10:29 +00:00
renovate[bot]
3a84b3f262 fix(deps): update dependency openai to v2.34.0 (#7594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 17:48:09 +00:00
renovate[bot]
a616e14bf9 fix(deps): update dependency authlib to v1.7.1 (#7593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 13:01:06 +00:00
renovate[bot]
b902d2cd98 chore(deps): update node.js to 050bf2b (#7592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 13:00:23 +00:00
renovate[bot]
565736e116 chore(deps): update node.js to 34f0eb9 (#7590)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 00:58:25 +00:00
renovate[bot]
7f29efc0e4 chore(deps): update dependency types-requests to v2.33.0.20260503 (#7587)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08 05:46:11 +00:00
Hayden
743c15a981 chore(l10n): New Crowdin updates (#7571)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-06 22:29:26 -05:00
renovate[bot]
3be9193590 chore(deps): update dependency mypy to v2 (#7584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-06 21:06:18 +00:00
mealie-commit-bot[bot]
c880c0865b chore: bump version to v3.17.0 2026-05-06 18:40:44 +00:00
Michael Genson
294238f183 fix: Adjust ingredient section spacing (#7580) 2026-05-06 11:57:27 -05:00
renovate[bot]
985b656d3f chore(deps): update dependency axios to v1.15.2 [security] (#7579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-06 15:31:14 +00:00
miah
09c2a0b2ad feat: Shopping list / Swipe to check off (#7118)
Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-06 10:31:33 -05:00
renovate[bot]
f2b087730e fix(deps): update dependency pydantic-settings to v2.14.0 (#7534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-06 14:56:01 +00:00
miah
e71b31e9cc feat: Improve add shopping list item form (#7091)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Michael Genson <genson.michael@gmail.com>
2026-05-04 11:15:01 -05:00
Hayden
41a9a1e018 chore(l10n): New Crowdin updates (#7558)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-05-03 17:54:02 +00:00
renovate[bot]
7b2372edfc fix(deps): update dependency openai to v2.33.0 (#7570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-03 14:23:17 +00:00
mealie-actions[bot]
65f109dee4 chore(l10n): Crowdin locale sync (#7569)
Co-authored-by: GitHub Action <action@github.com>
2026-05-03 03:10:04 +00:00
renovate[bot]
8dc85640e1 fix(deps): update dependency python-multipart to v0.0.27 (#7567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-02 12:48:26 +00:00
renovate[bot]
6c5f1c2413 fix(deps): update dependency apprise to v1.10.0 (#7566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-01 16:55:00 +00:00
Zdenek Stursa
bc3ae3c6c0 fix: restore create-item button in recipe dropdowns (categories, tags, tools) (#7564)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 15:16:27 +00:00
garlic-hub
5b37eb012c fix: Don't hit authenticated endpoints when logged out (#7563) 2026-04-30 04:21:48 +00:00
renovate[bot]
f354f12853 fix(deps): update dependency tzdata to v2026.2 (#7560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 20:00:29 +00:00
renovate[bot]
062484dec9 chore(deps): update dependency ruff to v0.15.12 (#7559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-29 19:59:26 +00:00
renovate[bot]
b5d991c516 fix(deps): update dependency fastapi to v0.136.1 (#7556)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-28 19:14:55 +00:00
renovate[bot]
b60aeed8dc fix(deps): update dependency uvicorn to v0.46.0 (#7553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-28 11:47:35 +00:00
renovate[bot]
1f42ba4934 chore(deps): update dependency pre-commit to v4.6.0 (#7547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-26 21:12:20 +00:00
Hayden
32e0404564 chore(l10n): New Crowdin updates (#7546) 2026-04-26 20:47:51 +00:00
renovate[bot]
a1a26b23c4 chore(deps): update dependency mypy to v1.20.2 (#7544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-26 18:15:38 +00:00
renovate[bot]
e66c0dea58 fix(deps): update dependency uvicorn to v0.45.0 (#7543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-26 14:51:00 +00:00
mealie-actions[bot]
fb5b028b92 chore(l10n): Crowdin locale sync (#7541)
Co-authored-by: GitHub Action <action@github.com>
2026-04-26 03:08:57 +00:00
renovate[bot]
2854449213 fix(deps): update dependency psycopg2-binary to v2.9.12 (#7539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-26 01:15:14 +00:00
Hayden
1bd423d741 chore(l10n): New Crowdin updates (#7536) 2026-04-25 20:16:11 +00:00
renovate[bot]
a754693787 fix(deps): update dependency pydantic to v2.13.3 (#7533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-25 15:09:02 +00:00
Hayden
176587079f chore(l10n): New Crowdin updates (#7526) 2026-04-24 00:35:23 +00:00
renovate[bot]
718d232517 fix(deps): update dependency authlib to v1.7.0 (#7525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 13:34:34 +00:00
Hayden
072f93d02d chore(l10n): New Crowdin updates (#7523) 2026-04-22 22:39:22 +00:00
renovate[bot]
70749b740a chore(deps): update node.js to e989123 (#7520)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 18:45:07 +00:00
renovate[bot]
60daa3b4e2 chore(deps): update node.js to 91447bc (#7519)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 12:11:41 +00:00
renovate[bot]
2da78b4fb5 fix(deps): update dependency pydantic to v2.13.2 (#7517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 11:49:47 +00:00
renovate[bot]
a5daaaab9e chore(deps): update node.js to 807109d (#7516)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 11:48:43 +00:00
Hayden
ad77b9851f chore(l10n): New Crowdin updates (#7515) 2026-04-22 10:42:56 +00:00
renovate[bot]
3db94d876c fix(deps): update dependency lxml to v6.1.0 [security] (#7513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 00:08:20 +00:00
renovate[bot]
39529ed606 chore(deps): update dependency ruff to v0.15.11 (#7514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 23:45:55 +00:00
Zdenek Stursa
b3fd2ccb33 fix: add missing search bar to Recipe Data management page (#7504)
Co-authored-by: Zdenek <tvuj-email@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-04-21 17:20:08 +00:00
Zdenek Stursa
fce0538671 fix: pressing Enter in dialogs now confirms instead of silently closing (#7503)
Co-authored-by: Zdenek <tvuj-email@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-04-21 10:03:31 -05:00
Ben Harper
b3ce0faf26 docs: document necessity of forwarded-allow-ips with OIDC behind reverse-proxy https in oidc-v2.md (#7424)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-04-21 14:30:04 +00:00
BadCo-NZ
870b793d5f docs: Enhance BASE_URL description in backend config (#7449)
Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-04-21 09:14:34 -05:00
renovate[bot]
26dfeac956 fix(deps): update dependency fastapi to v0.136.0 (#7511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 12:52:00 +00:00
Hayden
cfb60228f7 chore(l10n): New Crowdin updates (#7509) 2026-04-21 10:24:51 +00:00
Xavier L.
c9a0cac055 fix: Allow user-configurable OIDC timeout (#7496) 2026-04-21 03:22:36 +00:00
renovate[bot]
83bc2f3889 fix(deps): update dependency openai to v2.32.0 (#7507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-20 22:49:17 +00:00
Hayden
0ffc1e7bf7 chore(l10n): New Crowdin updates (#7506) 2026-04-20 22:25:35 +00:00
renovate[bot]
e166baa33c fix(deps): update dependency pydantic to v2.13.1 (#7505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-20 17:23:35 +00:00
Hayden
3e25005ea6 chore(l10n): New Crowdin updates (#7502) 2026-04-20 10:16:42 +00:00
Hayden
c92ebf2099 chore(l10n): New Crowdin updates (#7500) 2026-04-19 21:27:03 +00:00
Zdenek Stursa
8e429834af fix: use correct title and icon on Recipe Actions data page (#7498)
Co-authored-by: Zdenek <tvuj-email@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:04:38 +00:00
mealie-actions[bot]
2ca5694391 chore(l10n): Crowdin locale sync (#7497)
Co-authored-by: GitHub Action <action@github.com>
2026-04-19 03:08:18 +00:00
Michael Genson
8d8987ab05 docs: Update recipe creation docs (#7494) 2026-04-18 17:50:26 -05:00
Zdenek Stursa
372474ea2b fix: prevent delete-image dialog from reopening in a loop inside v-menu (#7469)
Co-authored-by: Zdenek <tvuj-email@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:39:43 +00:00
renovate[bot]
5b93129368 fix(deps): update dependency pydantic to v2.13.0 (#7492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-18 14:27:40 +00:00
renovate[bot]
ffeb4dceaf chore(deps): update dependency mypy to v1.20.1 (#7490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-18 05:55:14 +00:00
mealie-commit-bot[bot]
5fc4851ef5 chore: bump version to v3.16.0 2026-04-17 20:59:57 +00:00
Michael Genson
d9e933d5ae fix: Misc frontend layout fixes (#7487) 2026-04-17 12:28:13 -05:00
renovate[bot]
0a07835338 fix(deps): update dependency lxml to v6.0.4 (#7485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 16:46:53 +00:00
Michael Genson
7a85ea6ae9 chore: Update yarn deps (#7486) 2026-04-17 11:58:19 -05:00
renovate[bot]
c4c60f1645 fix(deps): update dependency authlib to v1.6.11 [security] (#7481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-17 16:46:22 +00:00
Zdenek Stursa
9f7ba8dc08 fix: preserve ingredient section titles when parsing recipe ingredients (#7483)
Co-authored-by: Zdenek <tvuj-email@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 16:44:45 +00:00
Michael Genson
c4799ceb9e dev: Enable lockfile maintenance and update deps (#7484) 2026-04-17 11:33:50 -05:00
Michael Genson
828be095a2 fix: Blank query filter builder fields (#7480) 2026-04-16 19:11:05 -05:00
renovate[bot]
18718fb647 chore(deps): update node.js to 33cf7f0 (#7478)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 15:57:22 +00:00
Brian Choromanski
fb545962dd feat: Migrate PWA manifest to backend (#7331)
Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-04-16 10:59:21 -05:00
Brian Choromanski
781a08ef54 docs: Added copy button to codeblocks (#7343) 2026-04-16 15:34:28 +00:00
167 changed files with 6671 additions and 5737 deletions

View File

@@ -6,4 +6,6 @@ Since this software is still considered beta/WIP support is always only given fo
## Reporting a Vulnerability
For general security vulnerabilities you're welcome to open a GitHub issues or contribute a fix. If you feel the vulnerability should not be disclosed you can open a generic issue on GitHub and email to the details to [ob92oy0sl@mozmail.com](mailto:ob92oy0sl@mozmail.com) which is monitored by the maintainer.
This repository has [private vulnerability reporting](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/privately-reporting-a-security-vulnerability) enabled. To confidentially report a security issue, click the **"Report a vulnerability"** button on the [Security tab](../../security/advisories/new) of this repository. This allows you to submit details directly to the maintainers without public disclosure.
For non-sensitive issues or general feedback, feel free to open a GitHub issue or contribute a fix via pull request.

View File

@@ -1,7 +1,7 @@
###############################################
# Frontend Build
###############################################
FROM node:24@sha256:80fc934952c8f1b2b4d39907af7211f8a9fff1a4c2cf673fb49099292c251cec \
FROM node:24@sha256:050bf2bbe33c1d6754e060bec89378a79ed831f04a7bb1a53fe45e997df7b3bb \
AS frontend-builder
WORKDIR /frontend

View File

@@ -42,6 +42,10 @@ Before you can start using OIDC Authentication, you must first configure a new c
http://localhost:9091/login
https://mealie.example.com/login
If you are hosting Mealie behind a reverse proxy (nginx, Caddy, ...) to terminate TLS, make sure to start Mealie's Gunicorn server
with `--forwarded-allow-ips=<ip-of-proxy>`, otherwise the `X-Forwarded-*` headers will be ignored and the generated OIDC redirect
URI will use the wrong scheme (http instead of https). This will lead to authentication errors with strict OIDC providers.
3. Configure allowed scopes
The scopes required are `openid profile email`

View File

@@ -6,10 +6,16 @@
### Creating Recipes
Mealie offers several ways to create recipes:
- **Recipe Scraper:** Create recipes from hundreds of websites by simply providing a URL.
- **Image Import:** Upload an image of a written or typed recipe and Mealie will use OCR to import it.
- **Video URL Import:** Provide a video URL (e.g., YouTube) and Mealie will transcribe the audio and parse the recipe.
- **Recipe HTML or JSON:** Copy/paste structured HTML or JSON and Mealie can import it.
- **Manual Editor:** Create recipes from scratch using the integrated editor.
Mealie's [AI integration](./installation/open-ai.md) greatly expands the ways you can create recipes:
- **Image Import:** Upload an image of a written or typed recipe and Mealie will use OCR and AI to import it.
- **Video URL Import:** Provide a video URL (e.g., YouTube) and Mealie will transcribe the audio and turn it into a recipe.
[Creation Demo](https://demo.mealie.io/g/home/r/create/url){ .md-button .md-button--primary .align-right }
### Importing Recipes

View File

@@ -10,7 +10,7 @@
| PGID | 911 | GroupID permissions between host OS and container |
| DEFAULT_GROUP | Home | The default group for users |
| DEFAULT_HOUSEHOLD | Family | The default household for users in each group |
| BASE_URL | http://localhost:8080 | Used for Notifications |
| BASE_URL | http://localhost:8080 | Used for notifications and the OIDC callback url |
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid. Must be <= 9600 (400 days, in hours). |
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
| API_DOCS | True | Turns on/off access to the API documentation locally |
@@ -114,6 +114,7 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md)
| OIDC_GROUPS_CLAIM | groups | Optional if not using `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP`. This is the claim Mealie will request from your IdP and will use to compare to `OIDC_USER_GROUP` or `OIDC_ADMIN_GROUP` to allow the user to log in to Mealie or is set as an admin. **Your IdP must be configured to grant this claim** |
| OIDC_SCOPES_OVERRIDE | None | Advanced configuration used to override the scopes requested from the IdP. **Most users won't need to change this**. At a minimum, 'openid profile email' are required. |
| OIDC_TLS_CACERTFILE | None | File path to Certificate Authority used to verify server certificate (e.g. `/path/to/ca.crt`) |
| OIDC_CLIENT_TIMEOUT | default | Configures the timeout value of the httpx client used for OIDC communications. If set to the string `default`, does not configure the value (uses the library's default of 5.0s). If set to the string `None`, disables the timeout entirely. If set to a numeric value, uses that as the timeout. |
### OpenAI

View File

@@ -31,7 +31,7 @@ To deploy mealie on your local network, it is highly recommended to use Docker t
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:v3.15.2`
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.18.0`
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
4. Restart the container

View File

@@ -10,7 +10,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
```yaml
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v3.15.2 # (3)
image: ghcr.io/mealie-recipes/mealie:v3.18.0 # (3)
container_name: mealie
restart: always
ports:

View File

@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
```yaml
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v3.15.2 # (3)
image: ghcr.io/mealie-recipes/mealie:v3.18.0 # (3)
container_name: mealie
restart: always
ports:

View File

@@ -19,6 +19,7 @@ theme:
custom_dir: docs/overrides
features:
- content.code.annotate
- content.code.copy
- navigation.top
- navigation.instant
- navigation.expand

View File

@@ -20,16 +20,12 @@
max-width: 1100px !important;
}
.theme--dark.v-application {
background-color: rgb(var(--v-theme-background, 30, 30, 30)) !important;
.v-theme--dark.v-application {
background-color: rgb(var(--v-theme-background)) !important;
}
.theme--dark.v-navigation-drawer {
background-color: rgb(var(--v-theme-background, 30, 30, 30)) !important;
}
.theme--dark.v-card {
background-color: #1e1e1e !important;
.v-theme--dark .v-navigation-drawer {
background-color: rgb(var(--v-theme-background)) !important;
}
.left-border {

View File

@@ -41,19 +41,14 @@
>
<v-select
v-if="index"
:model-value="field.logicalOperator"
:model-value="field.logicalOperator?.value"
:items="[logOps.AND, logOps.OR]"
item-title="label"
item-value="value"
variant="underlined"
class="text-center"
@update:model-value="setLogicalOperatorValue(field, index, $event as unknown as LogicalOperator)"
>
<template #chip="{ item }">
<span :class="config.select.textClass" style="width: 100%;">
{{ item.raw.label }}
</span>
</template>
</v-select>
/>
</v-col>
<!-- left parenthesis -->
@@ -67,14 +62,9 @@
:model-value="field.leftParenthesis"
:items="['', '(', '((', '(((']"
variant="underlined"
class="text-center"
@update:model-value="setLeftParenthesisValue(field, index, $event)"
>
<template #chip="{ item }">
<span :class="config.select.textClass" style="width: 100%;">
{{ item.raw }}
</span>
</template>
</v-select>
/>
</v-col>
<!-- field name -->
@@ -84,19 +74,14 @@
:class="config.col.class"
>
<v-select
chips
:model-value="field.label"
:items="fieldDefs"
variant="underlined"
item-title="label"
item-value="label"
class="text-center"
@update:model-value="setField(index, $event)"
>
<template #chip="{ item }">
<span :class="config.select.textClass" style="width: 100%;">
{{ item.raw.label }}
</span>
</template>
</v-select>
/>
</v-col>
<!-- relational operator -->
@@ -107,19 +92,14 @@
>
<v-select
v-if="field.type !== 'boolean'"
:model-value="field.relationalOperatorValue"
:model-value="field.relationalOperatorValue?.value"
:items="field.relationalOperatorChoices"
item-title="label"
item-value="value"
variant="underlined"
class="text-center"
@update:model-value="setRelationalOperatorValue(field, index, $event as unknown as RelationalKeyword | RelationalOperator)"
>
<template #chip="{ item }">
<span :class="config.select.textClass" style="width: 100%;">
{{ item.raw.label }}
</span>
</template>
</v-select>
/>
</v-col>
<!-- field value -->
@@ -275,14 +255,9 @@
:model-value="field.rightParenthesis"
:items="['', ')', '))', ')))']"
variant="underlined"
class="text-center"
@update:model-value="setRightParenthesisValue(field, index, $event)"
>
<template #chip="{ item }">
<span :class="config.select.textClass" style="width: 100%;">
{{ item.raw }}
</span>
</template>
</v-select>
/>
</v-col>
<!-- field actions -->
@@ -723,9 +698,6 @@ const config = computed(() => {
col: {
class: "d-flex justify-center align-end py-0",
},
select: {
textClass: "d-flex justify-center text-center",
},
items: {
icon: {
cols: (_index: number) => 2,

View File

@@ -32,7 +32,6 @@
density="compact"
:label="$t('recipe.recipe-name')"
autofocus
@keyup.enter="duplicateRecipe()"
/>
</v-card-text>
</BaseDialog>

View File

@@ -9,6 +9,7 @@
:items-per-page="15"
class="elevation-0"
:loading="loading"
:search="search"
return-object
>
<template #[`item.name`]="{ item }">
@@ -86,6 +87,7 @@ interface Props {
loading?: boolean;
recipes?: Recipe[];
showHeaders?: ShowHeaders;
search?: string;
}
const props = withDefaults(defineProps<Props>(), {
loading: false,

View File

@@ -321,7 +321,7 @@ async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) {
const householdsWithFood = subIng.food?.householdsWithIngredientFood || [];
ownIngs.push({
checked: !householdsWithFood.includes(currentHouseholdSlug.value),
ingredient: { ...subIng, quantity: (ing.quantity || 1) * (subIng.quantity || 1) },
ingredient: subIng,
});
}
}

View File

@@ -1,5 +1,17 @@
<template>
<div class="text-center">
<BaseDialog
v-model="dialogDeleteImage"
:title="$t('recipe.delete-image')"
:icon="$globals.icons.alertCircle"
color="error"
can-delete
@delete="deleteImage"
>
<v-card-text>
{{ $t("recipe.delete-image-confirmation") }}
</v-card-text>
</BaseDialog>
<v-menu
v-model="menu"
offset-y
@@ -37,18 +49,6 @@
delete
@click="dialogDeleteImage = true"
/>
<BaseDialog
v-model="dialogDeleteImage"
:title="$t('recipe.delete-image')"
:icon="$globals.icons.alertCircle"
color="error"
can-delete
@delete="deleteImage"
>
<v-card-text>
{{ $t("recipe.delete-image-confirmation") }}
</v-card-text>
</BaseDialog>
</div>
</v-card-title>
<v-card-text class="mt-n5">

View File

@@ -88,7 +88,7 @@
</div>
</template>
<template #append-item>
<div class="px-2">
<div v-if="showCreateUnit" class="px-2">
<BaseButton
block
size="small"
@@ -147,7 +147,7 @@
</div>
</template>
<template #append-item>
<div class="px-2">
<div v-if="showCreateFood" class="px-2">
<BaseButton
block
size="small"
@@ -344,6 +344,11 @@ const foodData = useFoodData();
const foodAutocomplete = ref<HTMLInputElement>();
const { search: foodSearch, filtered: filteredFoods } = useSearch(foodStore.store);
const showCreateFood = computed(() =>
!!foodSearch.value
&& !filteredFoods.value.some((f: any) => (f.name ?? "").toLowerCase() === foodSearch.value.toLowerCase()),
);
async function createAssignFood() {
foodData.data.name = foodSearch.value;
model.value.food = await foodStore.actions.createOne(foodData.data) || undefined;
@@ -376,6 +381,11 @@ const unitsData = useUnitData();
const unitAutocomplete = ref<HTMLInputElement>();
const { search: unitSearch, filtered: filteredUnits } = useSearch(unitStore.store);
const showCreateUnit = computed(() =>
!!unitSearch.value
&& !filteredUnits.value.some((u: any) => (u.name ?? "").toLowerCase() === unitSearch.value.toLowerCase()),
);
async function createAssignUnit() {
unitsData.data.name = unitSearch.value;
model.value.unit = await unitStore.actions.createOne(unitsData.data) || undefined;

View File

@@ -19,11 +19,11 @@
>
<h3
v-if="showTitleEditor[index]"
class="mt-2"
class="mt-4 mb-0"
>
{{ ingredient.title }}
</h3>
<v-divider v-if="showTitleEditor[index]" />
<v-divider v-if="showTitleEditor[index]" class="my-2" />
<v-list-item
density="compact"
class="pa-0"

View File

@@ -19,6 +19,7 @@
class="pa-0 ma-0"
@update:model-value="resetSearchInput"
@click:append="dialog = true"
@keyup.enter="handleEnter"
>
<template #chip="{ item, index }">
<v-chip
@@ -32,6 +33,26 @@
@click:close="removeByIndex(index)"
/>
</template>
<template
v-if="showAdd"
#no-data
>
<div class="caption text-center pb-2">
{{ $t("recipe.press-enter-to-create") }}
</div>
</template>
<template
v-if="showAdd && searchInput"
#append-item
>
<div class="px-2">
<BaseButton
block
size="small"
@click="createItem()"
/>
</div>
</template>
<template
v-if="showAdd"
#append
@@ -180,6 +201,32 @@ function appendCreated(item: any) {
selected.value = [...selected.value, item];
}
function handleEnter() {
if (!searchInput.value) {
return;
}
const exactMatch = items.value.some(
(item: any) => (item.name ?? "").toLowerCase() === searchInput.value.toLowerCase(),
);
if (!exactMatch) {
createItem();
}
}
async function createItem() {
if (!searchInput.value) {
return;
}
const actions = storeMap[props.selectorType].actions;
// @ts-expect-error different organizer types have different required fields
const newItem = await actions.createOne({ name: searchInput.value });
if (newItem) {
appendCreated(newItem);
}
searchInput.value = "";
}
const dialog = ref(false);
const searchInput = ref("");

View File

@@ -336,8 +336,16 @@ onMounted(() => {
}
});
// When set, the isEditMode watcher skips its URL cleanup because saveRecipe
// is navigating to a new slug that naturally omits ?edit=true.
const isNavigatingAfterRename = ref(false);
watch(isEditMode, (newVal) => {
if (!newVal) {
if (isNavigatingAfterRename.value) {
isNavigatingAfterRename.value = false;
return;
}
paramsEdit.value = undefined;
}
});
@@ -355,13 +363,17 @@ watch(isParsing, () => {
async function saveRecipe() {
const { data, error } = await api.recipes.updateOne(recipe.value.slug, recipe.value);
if (!error) {
if (data?.slug && data.slug !== route.params.slug) {
isNavigatingAfterRename.value = true;
}
setMode(PageMode.VIEW);
}
if (data?.slug) {
router.push(`/g/${groupSlug.value}/r/` + data.slug);
recipe.value = data as NoUndefinedField<Recipe>;
// Update the snapshot after successful save
originalRecipe.value = deepCopy(recipe.value);
if (data.slug !== route.params.slug) {
router.replace(`/g/${groupSlug.value}/r/` + data.slug);
}
}
}

View File

@@ -371,14 +371,18 @@ async function parseIngredients() {
}
state.loading.parser = true;
try {
const ingsAsString = props.ingredients
.filter(ing => !ing.referencedRecipe)
.map(ing => ingredientToParserString(ing));
const filteredIngredients = props.ingredients.filter(ing => !ing.referencedRecipe);
const ingsAsString = filteredIngredients.map(ing => ingredientToParserString(ing));
const { data, error } = await api.recipes.parseIngredients(parser.value, ingsAsString);
if (error || !data) {
throw new Error("Failed to parse ingredients");
}
parsedIngs.value = data;
// Restore section titles from original ingredients — the parser doesn't return them
data.forEach((parsed, index) => {
parsed.ingredient.title = filteredIngredients[index]?.title || "";
});
const parsed = data ?? [];
const recipeRefs = props.ingredients.filter(ing => ing.referencedRecipe).map(ing => ({
input: ing.note || "",

View File

@@ -1,6 +1,6 @@
<template>
<div style="height: 100%;">
<v-row class="my-0 mx-7">
<v-row class="mb-0 mt-3 mx-7">
<v-spacer />
<v-col class="text-right">
<!-- Filters -->
@@ -44,6 +44,7 @@
:model-value="option.checked"
color="primary"
readonly
hide-details
@click="toggleEventTypeOption(option.value)"
>
<template #label>

View File

@@ -28,7 +28,6 @@
<v-col v-else cols="9" style="margin: auto; text-align: center">
{{ event.subject }}
</v-col>
<v-spacer />
<v-col :cols="useMobileFormat ? 'auto' : '1'" class="px-0 pt-0">
<RecipeTimelineContextMenu
v-if="currentUser && currentUser.id == event.userId && event.eventType != 'system'"

View File

@@ -0,0 +1,127 @@
<template>
<v-navigation-drawer
permanent
rounded="t-xl"
location="bottom"
class="pa-4 pt-2 mb-0"
width="300"
rail-width="85"
:rail="rail"
elevation="4"
>
<div class="d-flex flex-column ga-3">
<v-card-actions class="pa-0">
<div class="position-relative" style="flex: 1;">
<InputLabelType
ref="foodInputRef"
v-model="listItem.food"
v-model:item-id="listItem.foodId!"
:items="foods"
:label="rail ? $t('shopping-list.add-item') : $t('shopping-list.food')"
:icon="$globals.icons.foods"
:style="rail ? 'margin-inline: 3px;' : undefined"
:search="rail"
:menu-props="{ location: menuDirection }"
create
@create="createAssignFood"
/>
<!-- Intercept clicks when collapsed so the drawer expands before the autocomplete opens -->
<div
v-if="rail"
class="position-absolute"
style="inset: 0; cursor: text;"
@click="expandAndFocus"
/>
</div>
<BaseButtonGroup
v-if="!rail"
:buttons="[
{
icon: $globals.icons.close,
text: $t('general.cancel'),
event: 'cancel',
},
{
icon: $globals.icons.save,
text: $t('general.save'),
event: 'save',
},
]"
@save="$emit('save')"
@cancel="rail = true; $emit('cancel')"
/>
</v-card-actions>
<ShoppingListItemDetails
v-if="!rail"
v-model="listItem"
:labels="labels"
:units="units"
@save="$emit('save')"
/>
</div>
</v-navigation-drawer>
</template>
<script setup lang="ts">
import { useShoppingListItemEditor } from "~/composables/shopping-list-page/use-shopping-list-item-editor";
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import type { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
import ShoppingListItemDetails from "./ShoppingListItemDetails.vue";
// modelValue as reactive v-model
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
defineProps({
labels: {
type: Array as () => MultiPurposeLabelOut[],
required: true,
},
units: {
type: Array as () => IngredientUnit[],
required: true,
},
foods: {
type: Array as () => IngredientFood[],
required: true,
},
});
defineEmits<{
(e: "save" | "cancel" | "delete"): void;
}>();
const { createAssignFood } = useShoppingListItemEditor(listItem);
const { smAndDown } = useDisplay();
const menuDirection = computed(() => smAndDown.value ? "top" : "bottom");
const foodInputRef = ref<{ focus: () => void } | null>(null);
const rail = ref(true);
async function expandAndFocus() {
rail.value = false;
await nextTick();
setTimeout(() => {
foodInputRef.value?.focus();
}, 200);
}
watch(
() => listItem.value.quantity,
(newQty) => {
if (!newQty) {
listItem.value.quantity = 0;
}
},
);
watch(
() => listItem.value.food,
(newFood) => {
listItem.value.label = newFood?.label || null;
listItem.value.labelId = listItem.value.label?.id || null;
},
);
</script>

View File

@@ -1,154 +1,180 @@
<template>
<v-container
v-if="!edit"
class="pa-0"
>
<v-row
no-gutters
class="flex-nowrap align-center"
<div style="overflow-x: hidden;">
<v-container
v-if="!edit"
class="pa-0"
:style="{
transform: `translateX(${isRtl ? -swiping : swiping}px)`,
transition: swiping === 0 ? 'transform 0.2s ease' : 'none',
opacity: swiping >= SWIPE_THRESHOLD ? 0.5 : 1,
}"
>
<v-col :cols="itemLabelCols">
<div class="d-flex align-center flex-nowrap">
<v-checkbox
:model-value="listItem.checked"
hide-details
density="compact"
class="mt-0 flex-shrink-0"
color="null"
@click="toggleChecked"
/>
<div
class="ml-2 text-truncate"
:class="listItem.checked ? 'strike-through' : ''"
style="min-width: 0;"
>
<RecipeIngredientListItem :ingredient="listItem" />
<v-row
v-touch="{
move: ({ originalEvent: { touches: [{ screenX, screenY }] } }) => {
swipeInfo.touchendX = screenX;
swipeInfo.touchendY = screenY;
},
start: ({ originalEvent: { touches: [{ screenX, screenY }] } }) => {
swipeInfo.touchstartX = screenX;
swipeInfo.touchstartY = screenY;
},
end: () => {
if (swiping < SWIPE_THRESHOLD) {
swipeInfo = {};
return;
}
swipeInfo = {};
toggleChecked();
},
}"
no-gutters
class="flex-nowrap align-center"
>
<v-col :cols="itemLabelCols">
<div class="d-flex align-center flex-nowrap">
<v-checkbox
:model-value="listItem.checked"
hide-details
density="compact"
class="mt-0 flex-shrink-0"
color="null"
@click="toggleChecked"
/>
<div
class="ml-2 text-truncate"
:class="listItem.checked ? 'strike-through' : ''"
style="min-width: 0;"
>
<RecipeIngredientListItem :ingredient="listItem" />
</div>
</div>
</div>
</v-col>
<v-spacer />
<v-col
cols="auto"
class="text-right"
>
<div
v-if="!listItem.checked"
style="min-width: 72px"
</v-col>
<v-spacer />
<v-col
cols="auto"
class="text-right"
>
<v-menu
offset-x
start
min-width="125px"
<div
v-if="!listItem.checked"
style="min-width: 72px"
>
<template #activator="{ props: hoverProps }">
<v-tooltip
v-if="recipeList && recipeList.length"
open-delay="200"
transition="slide-x-reverse-transition"
density="compact"
location="end"
content-class="text-caption"
>
<template #activator="{ props: tooltipProps }">
<v-btn
size="small"
variant="text"
class="ml-2"
icon
v-bind="tooltipProps"
@click="displayRecipeRefs = !displayRecipeRefs"
>
<v-icon>
{{ $globals.icons.potSteam }}
</v-icon>
</v-btn>
</template>
<span>Toggle Recipes</span>
</v-tooltip>
<v-btn
size="small"
variant="text"
class="ml-2"
icon
@click="toggleEdit(true)"
>
<v-icon>
{{ $globals.icons.edit }}
</v-icon>
</v-btn>
<v-btn
size="small"
variant="text"
class="handle"
icon
v-bind="hoverProps"
>
<v-icon>
{{ $globals.icons.arrowUpDown }}
</v-icon>
</v-btn>
</template>
<v-list density="compact">
<v-list-item
v-for="action in contextMenu"
:key="action.event"
density="compact"
@click="contextHandler(action.event)"
>
<v-list-item-title>
{{ action.text }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
</v-col>
</v-row>
<v-row
v-if="!listItem.checked && recipeList && recipeList.length && displayRecipeRefs"
no-gutters
class="mb-2"
>
<v-col
cols="auto"
style="width: 100%;"
<v-menu
offset-x
start
min-width="125px"
>
<template #activator="{ props: hoverProps }">
<v-tooltip
v-if="recipeList && recipeList.length"
open-delay="200"
transition="slide-x-reverse-transition"
density="compact"
location="end"
content-class="text-caption"
>
<template #activator="{ props: tooltipProps }">
<v-btn
size="small"
variant="text"
class="ml-2"
icon
v-bind="tooltipProps"
@click="displayRecipeRefs = !displayRecipeRefs"
>
<v-icon>
{{ $globals.icons.potSteam }}
</v-icon>
</v-btn>
</template>
<span>Toggle Recipes</span>
</v-tooltip>
<v-btn
size="small"
variant="text"
class="ml-2"
icon
@click="toggleEdit(true)"
>
<v-icon>
{{ $globals.icons.edit }}
</v-icon>
</v-btn>
<v-btn
size="small"
variant="text"
class="handle"
icon
v-bind="hoverProps"
>
<v-icon>
{{ $globals.icons.arrowUpDown }}
</v-icon>
</v-btn>
</template>
<v-list density="compact">
<v-list-item
v-for="action in contextMenu"
:key="action.event"
density="compact"
@click="contextHandler(action.event)"
>
<v-list-item-title>
{{ action.text }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
</v-col>
</v-row>
<v-row
v-if="!listItem.checked && recipeList && recipeList.length && displayRecipeRefs"
no-gutters
class="mb-2"
>
<RecipeList
:recipes="recipeList"
:list-item="listItem"
:disabled="isOffline"
size="small"
tile
/>
</v-col>
</v-row>
<v-row
v-if="listItem.checked"
no-gutters
class="mb-2"
<v-col
cols="auto"
style="width: 100%;"
>
<RecipeList
:recipes="recipeList"
:list-item="listItem"
:disabled="isOffline"
size="small"
tile
/>
</v-col>
</v-row>
<v-row
v-if="listItem.checked"
no-gutters
class="mb-2"
>
<v-col cols="auto">
<div class="text-caption font-weight-light font-italic">
{{ $t("shopping-list.completed-on", {
date: listItem.updatedAt ? $d(new Date(listItem.updatedAt)) : '',
}) }}
</div>
</v-col>
</v-row>
</v-container>
<div
v-else
class="mb-1 mt-6"
>
<v-col cols="auto">
<div class="text-caption font-weight-light font-italic">
{{ $t("shopping-list.completed-on", {
date: listItem.updatedAt ? $d(new Date(listItem.updatedAt)) : '',
}) }}
</div>
</v-col>
</v-row>
</v-container>
<div
v-else
class="mb-1 mt-6"
>
<ShoppingListItemEditor
v-model="localListItem"
:labels="labels"
:units="units"
:foods="foods"
@save="save"
@cancel="toggleEdit(false)"
@delete="$emit('delete')"
/>
<ShoppingListItemEditor
v-model="localListItem"
:labels="labels"
:units="units"
:foods="foods"
class="ma-2"
@save="save"
@cancel="toggleEdit(false)"
@delete="$emit('delete')"
/>
</div>
</div>
</template>
@@ -156,10 +182,10 @@
import { useOnline } from "@vueuse/core";
import RecipeIngredientListItem from "../Recipe/RecipeIngredientListItem.vue";
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
import type { ShoppingListItemOut } from "~/lib/api/types/household";
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import type { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
import type { IngredientUnit, IngredientFood, RecipeSummary } from "~/lib/api/types/recipe";
const model = defineModel<ShoppingListItemOut>({ type: Object as () => ShoppingListItemOut, required: true });
@@ -187,6 +213,10 @@ const emit = defineEmits<{
(e: "delete"): void;
}>();
const SWIPE_THRESHOLD = 50;
const SCROLL_THRESHOLD = 50;
const { isRtl } = useRtl();
const i18n = useI18n();
const displayRecipeRefs = ref(false);
const itemLabelCols = computed<string>(() => (model.value?.checked ? "auto" : "6"));
@@ -237,6 +267,24 @@ function save() {
edit.value = false;
}
const swipeInfo: Ref<{ touchstartX?: number; touchendX?: number; touchstartY?: number; touchendY?: number }> = ref({});
const swiping = computed(() => {
const { touchstartX, touchendX, touchstartY, touchendY } = swipeInfo.value ?? {};
if (touchstartX === undefined || touchendX === undefined) {
return 0;
}
const deltaX = isRtl.value ? touchstartX - touchendX : touchendX - touchstartX;
// If there's significant vertical movement, treat as a scroll gesture and ignore
if (touchstartY !== undefined && touchendY !== undefined) {
const deltaY = Math.abs(touchendY - touchstartY);
if (deltaY > SCROLL_THRESHOLD) {
return 0;
}
}
return Math.min(Math.max(0, deltaX), 100);
});
const recipeList = computed<RecipeSummary[]>(() => {
const ret: RecipeSummary[] = [];
if (!listItem.value.recipeReferences) return ret;

View File

@@ -0,0 +1,90 @@
<template>
<div class="d-flex ga-3">
<v-number-input
v-model="listItem.quantity"
hide-details
:label="$t('form.quantity-label-abbreviated')"
:min="0"
:precision="null"
control-variant="stacked"
style="flex: 1"
inset
/>
<InputLabelType
v-model="listItem.unit"
v-model:item-id="listItem.unitId!"
:items="units"
:label="$t('recipe.unit')"
:icon="$globals.icons.units"
:menu-props="{ location: menuDirection }"
style="flex: 3"
create
@create="createAssignUnit"
/>
</div>
<v-textarea
v-model="listItem.note"
hide-details
:label="$t('shopping-list.note')"
rows="1"
auto-grow
@keypress="handleNoteKeyPress"
/>
<div class="d-flex flex-wrap align-end ga-3">
<InputLabelType
v-model="listItem.label"
v-model:item-id="listItem.labelId!"
:items="labels"
:label="$t('shopping-list.label')"
:menu-props="{ location: menuDirection }"
style="flex: 1 0 200px"
/>
<BaseButton
v-if="listItem.labelId && listItem.food && listItem.labelId !== listItem.food.labelId"
small
color="info"
:icon="$globals.icons.tagArrowRight"
:text="$t('shopping-list.save-label')"
class="mt-2 align-items-flex-start"
style="flex-grow: 0"
@click="assignLabelToFood"
/>
<v-spacer />
</div>
</template>
<script setup lang="ts">
import { useShoppingListItemEditor } from "~/composables/shopping-list-page/use-shopping-list-item-editor";
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import type { IngredientUnit } from "~/lib/api/types/recipe";
// modelValue as reactive v-model
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
defineProps({
labels: {
type: Array as () => MultiPurposeLabelOut[],
required: true,
},
units: {
type: Array as () => IngredientUnit[],
required: true,
},
});
const emit = defineEmits<{ (e: "save"): void }>();
const { assignLabelToFood, createAssignUnit } = useShoppingListItemEditor(listItem);
const { smAndDown } = useDisplay();
const menuDirection = computed(() => smAndDown.value ? "top" : "bottom");
function handleNoteKeyPress(event: KeyboardEvent) {
// Save on Enter
if (!event.shiftKey && event.key === "Enter") {
event.preventDefault();
emit("save");
}
}
</script>

View File

@@ -1,112 +1,60 @@
<template>
<div>
<v-card variant="outlined">
<v-card-text class="pb-3 pt-1">
<div class="d-md-flex align-center mb-2" style="gap: 20px">
<div>
<v-number-input
v-model="listItem.quantity"
hide-details
:label="$t('form.quantity-label-abbreviated')"
:min="0"
:precision="null"
control-variant="stacked"
inset
style="width: 100px;"
/>
</div>
<InputLabelType
v-model="listItem.unit"
v-model:item-id="listItem.unitId!"
:items="units"
:label="$t('recipe.unit')"
:icon="$globals.icons.units"
create
@create="createAssignUnit"
/>
<InputLabelType
v-model="listItem.food"
v-model:item-id="listItem.foodId!"
:items="foods"
:label="$t('shopping-list.food')"
:icon="$globals.icons.foods"
:autofocus="autoFocus === 'food'"
create
@create="createAssignFood"
/>
</div>
<div class="d-md-flex align-center" style="gap: 20px">
<v-textarea
v-model="listItem.note"
hide-details
:label="$t('shopping-list.note')"
rows="1"
auto-grow
:autofocus="autoFocus === 'note'"
@keypress="handleNoteKeyPress"
/>
</div>
<div class="d-flex flex-wrap align-end" style="gap: 20px">
<div class="d-flex align-end">
<div style="max-width: 300px" class="mt-3 mr-auto">
<InputLabelType
v-model="listItem.label"
v-model:item-id="listItem.labelId!"
:items="labels"
:label="$t('shopping-list.label')"
width="250"
/>
</div>
</div>
<BaseButton
v-if="listItem.labelId && listItem.food && listItem.labelId !== listItem.food.labelId"
small
color="info"
:icon="$globals.icons.tagArrowRight"
:text="$t('shopping-list.save-label')"
class="mt-2 align-items-flex-start"
@click="assignLabelToFood"
/>
<v-spacer />
</div>
</v-card-text>
<v-card-actions class="ma-0 pt-0 pb-1 justify-end">
<BaseButtonGroup
:buttons="[
...(allowDelete
? [
{
icon: $globals.icons.delete,
text: $t('general.delete'),
event: 'delete',
},
]
: []),
{
icon: $globals.icons.close,
text: $t('general.cancel'),
event: 'cancel',
},
{
icon: $globals.icons.save,
text: $t('general.save'),
event: 'save',
},
]"
@save="$emit('save')"
@cancel="$emit('cancel')"
@delete="$emit('delete')"
/>
</v-card-actions>
</v-card>
</div>
<v-card variant="elevated" class="pa-2" border="primary s-lg opacity-100">
<div class="d-flex flex-column ga-3">
<InputLabelType
v-model="listItem.food"
v-model:item-id="listItem.foodId!"
:items="foods"
:label="$t('shopping-list.food')"
:icon="$globals.icons.foods"
:autofocus="autoFocus === 'food'"
create
@create="createAssignFood"
/>
<ShoppingListItemDetails
v-model="listItem"
:labels="labels"
:units="units"
@save="$emit('save')"
/>
</div>
<v-card-actions class="justify-end pa-0">
<BaseButtonGroup
:buttons="[
...(allowDelete
? [
{
icon: $globals.icons.delete,
text: $t('general.delete'),
event: 'delete',
},
]
: []),
{
icon: $globals.icons.close,
text: $t('general.cancel'),
event: 'cancel',
},
{
icon: $globals.icons.save,
text: $t('general.save'),
event: 'save',
},
]"
@save="$emit('save')"
@cancel="$emit('cancel')"
@delete="$emit('delete')"
/>
</v-card-actions>
</v-card>
</template>
<script setup lang="ts">
import { useShoppingListItemEditor } from "~/composables/shopping-list-page/use-shopping-list-item-editor";
import type { ShoppingListItemCreate, ShoppingListItemOut } from "~/lib/api/types/household";
import type { MultiPurposeLabelOut } from "~/lib/api/types/labels";
import type { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
import { useFoodStore, useFoodData, useUnitStore, useUnitData } from "~/composables/store";
import ShoppingListItemDetails from "./ShoppingListItemDetails.vue";
// modelValue as reactive v-model
const listItem = defineModel<ShoppingListItemCreate | ShoppingListItemOut>({ required: true });
@@ -132,16 +80,11 @@ defineProps({
});
// const emit = defineEmits<["save", "cancel", "delete"]>();
const emit = defineEmits<{
(e: "save", item: ShoppingListItemOut): void;
(e: "cancel" | "delete"): void;
defineEmits<{
(e: "save" | "cancel" | "delete"): void;
}>();
const foodStore = useFoodStore();
const foodData = useFoodData();
const unitStore = useUnitStore();
const unitData = useUnitData();
const { createAssignFood } = useShoppingListItemEditor(listItem);
watch(
() => listItem.value.quantity,
@@ -161,49 +104,4 @@ watch(
);
const autoFocus = computed(() => (!listItem.value.food && listItem.value.note ? "note" : "food"));
async function createAssignFood(val: string) {
// keep UI reactive
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
listItem.value.food ? (listItem.value.food.name = val) : (listItem.value.food = { name: val } as any);
foodData.data.name = val;
const newFood = await foodStore.actions.createOne(foodData.data);
if (newFood) {
listItem.value.food = newFood;
listItem.value.foodId = newFood.id;
}
foodData.reset();
}
async function createAssignUnit(val: string) {
// keep UI reactive
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
listItem.value.unit ? (listItem.value.unit.name = val) : (listItem.value.unit = { name: val } as any);
unitData.data.name = val;
const newUnit = await unitStore.actions.createOne(unitData.data);
if (newUnit) {
listItem.value.unit = newUnit;
listItem.value.unitId = newUnit.id;
}
unitData.reset();
}
async function assignLabelToFood() {
if (!(listItem.value.food && listItem.value.foodId && listItem.value.labelId)) {
return;
}
listItem.value.food.labelId = listItem.value.labelId;
await foodStore.actions.updateOne(listItem.value.food);
}
function handleNoteKeyPress(event: KeyboardEvent) {
const e = event as KeyboardEvent & { key: string; shiftKey: boolean };
if (!e.shiftKey && e.key === "Enter") {
e.preventDefault();
emit("save");
}
}
</script>

View File

@@ -110,7 +110,7 @@ const route = useRoute();
const groupSlug = computed(() => route.params.groupSlug as string || auth.user.value?.groupSlug || "");
const cookbookPreferences = useCookbookPreferences();
const ownCookbookStore = useCookbookStore(i18n);
const ownCookbookStore = computed(() => isOwnGroup.value ? useCookbookStore(i18n) : null);
const publicCookbookStoreCache = ref<Record<string, ReturnType<typeof usePublicCookbookStore>>>({});
function getPublicCookbookStore(slug: string) {
@@ -121,8 +121,8 @@ function getPublicCookbookStore(slug: string) {
}
const cookbooks = computed(() => {
if (isOwnGroup.value) {
return ownCookbookStore.store.value;
if (ownCookbookStore.value) {
return ownCookbookStore.value.store.value;
}
else if (groupSlug.value) {
const publicStore = getPublicCookbookStore(groupSlug.value);

View File

@@ -4,7 +4,7 @@
v-model="toastAlert.open"
location="top"
:color="toastAlert.color"
timeout="2000"
:timeout="toastAlert.timeout ?? 2000"
>
<v-icon
v-if="icon"
@@ -19,9 +19,12 @@
<template #actions>
<v-btn
variant="text"
@click="toastAlert.open = false"
@click="() => {
toastAlert.action?.onClick();
toastAlert.open = false
}"
>
{{ $t('general.close') }}
{{ toastAlert.action?.message ?? $t('general.close') }}
</v-btn>
</template>
</v-snackbar>

View File

@@ -192,6 +192,14 @@ function submitOnEnter() {
return;
}
if (props.canConfirm) {
if (!props.submitDisabled) {
emit("confirm");
dialog.value = false;
}
return;
}
submitEvent();
}

View File

@@ -7,12 +7,15 @@
item-title="name"
return-object
:items="filteredItems"
:prepend-icon="icon || $globals.icons.tags"
:prepend-inner-icon="icon || (search ? $globals.icons.search : $globals.icons.tags)"
:menu-icon="search ? '' : undefined"
:rounded="search ? true : '4px'"
:custom-filter="() => true"
:variant="search ? 'solo-filled' : undefined"
color="primary"
auto-select-first
clearable
color="primary"
hide-details
:custom-filter="() => true"
@keyup.enter="emitCreate"
>
<template
@@ -55,6 +58,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
search: {
type: Boolean,
default: false,
},
});
const emit = defineEmits<{
@@ -86,4 +93,8 @@ function emitCreate() {
emit("create", searchInput.value);
autocompleteRef.value?.blur();
}
defineExpose({
focus: () => autocompleteRef.value?.focus(),
});
</script>

View File

@@ -14,17 +14,25 @@
<script setup lang="ts">
import { useWakeLock } from "@vueuse/core";
import { useUserExperiencePreferences } from "~/composables/use-users/preferences";
const { isSupported: wakeIsSupported, isActive, request, release } = useWakeLock();
const userExperiencePreferences = useUserExperiencePreferences();
function handleLock() {
if (userExperiencePreferences.value.lockScreen) {
lockScreen();
}
else {
unlockScreen();
}
}
const wakeLock = computed({
get: () => isActive.value,
get: () => userExperiencePreferences.value.lockScreen,
set: () => {
if (isActive.value) {
unlockScreen();
}
else {
lockScreen();
}
userExperiencePreferences.value.lockScreen = !userExperiencePreferences.value.lockScreen;
handleLock();
},
});
async function lockScreen() {
@@ -34,11 +42,11 @@ async function lockScreen() {
}
}
async function unlockScreen() {
if (wakeIsSupported || isActive) {
if (wakeIsSupported || isActive.value) {
console.debug("Wake Lock Released");
await release();
}
}
onMounted(() => lockScreen());
onMounted(() => handleLock());
onUnmounted(() => unlockScreen());
</script>

View File

@@ -13,9 +13,10 @@ describe("useStoreActions", () => {
const mockStore = ref([]);
const mockLoading = ref(false);
const mockInitialized = ref(false);
test("deleteMany calls deleteOne for each ID and refreshes once", async () => {
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading);
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, mockInitialized);
mockApi.deleteOne = vi.fn().mockResolvedValue({ response: { data: {} } });
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
@@ -32,7 +33,7 @@ describe("useStoreActions", () => {
});
test("deleteMany handles empty array", async () => {
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading);
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, mockInitialized);
mockApi.deleteOne = vi.fn();
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
@@ -44,7 +45,7 @@ describe("useStoreActions", () => {
});
test("deleteMany sets loading state", async () => {
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading);
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, mockInitialized);
mockApi.deleteOne = vi.fn().mockResolvedValue({});
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
@@ -55,4 +56,25 @@ describe("useStoreActions", () => {
await promise;
expect(mockLoading.value).toBe(false);
});
test("refresh sets initialized to true even when store returns empty results", async () => {
const localInitialized = ref(false);
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, localInitialized);
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [] } });
expect(localInitialized.value).toBe(false);
await actions.refresh();
expect(localInitialized.value).toBe(true);
});
test("refresh sets initialized to true when store returns items", async () => {
const localInitialized = ref(false);
const actions = useStoreActions("test-store", mockApi, mockStore, mockLoading, localInitialized);
mockApi.getAll = vi.fn().mockResolvedValue({ data: { items: [{ id: "1", name: "item" }] } });
await actions.refresh();
expect(localInitialized.value).toBe(true);
});
});

View File

@@ -26,6 +26,7 @@ export function useReadOnlyActions<T extends BoundT>(
api: BaseCRUDAPIReadOnly<T>,
allRef: Ref<T[] | null> | null,
loading: Ref<boolean>,
initialized: Ref<boolean>,
defaultQueryParams: Record<string, QueryValue> = {},
): ReadOnlyStoreActions<T> {
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
@@ -69,6 +70,7 @@ export function useReadOnlyActions<T extends BoundT>(
allRef.value = data.items;
}
initialized.value = true;
loading.value = false;
}
@@ -89,6 +91,7 @@ export function useStoreActions<T extends BoundT>(
api: BaseCRUDAPI<unknown, T, unknown>,
allRef: Ref<T[] | null> | null,
loading: Ref<boolean>,
initialized: Ref<boolean>,
defaultQueryParams: Record<string, QueryValue> = {},
): StoreActions<T> {
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
@@ -132,6 +135,7 @@ export function useStoreActions<T extends BoundT>(
allRef.value = data.items;
}
initialized.value = true;
loading.value = false;
}

View File

@@ -16,10 +16,11 @@ export const useReadOnlyStore = function <T extends BoundT>(
storeKey: string,
store: Ref<T[]>,
loading: Ref<boolean>,
initialized: Ref<boolean>,
api: BaseCRUDAPIReadOnly<T>,
params = {} as Record<string, QueryValue>,
) {
const storeActions = useReadOnlyActions(`${storeKey}-store-readonly`, api, store, loading);
const storeActions = useReadOnlyActions(`${storeKey}-store-readonly`, api, store, loading, initialized);
const actions = {
...storeActions,
async refresh() {
@@ -27,11 +28,12 @@ export const useReadOnlyStore = function <T extends BoundT>(
},
flushStore() {
store.value = [];
initialized.value = false;
},
};
// initial hydration
if (!loading.value && !store.value.length) {
if (!loading.value && !initialized.value) {
actions.refresh();
}
@@ -42,10 +44,11 @@ export const useStore = function <T extends BoundT>(
storeKey: string,
store: Ref<T[]>,
loading: Ref<boolean>,
initialized: Ref<boolean>,
api: BaseCRUDAPI<unknown, T, unknown>,
params = {} as Record<string, QueryValue>,
) {
const storeActions = useStoreActions(`${storeKey}-store`, api, store, loading);
const storeActions = useStoreActions(`${storeKey}-store`, api, store, loading, initialized);
const actions = {
...storeActions,
async refresh() {
@@ -53,11 +56,12 @@ export const useStore = function <T extends BoundT>(
},
flushStore() {
store.value = [];
initialized.value = false;
},
};
// initial hydration
if (!loading.value && !store.value.length) {
if (!loading.value && !initialized.value) {
actions.refresh();
}

View File

@@ -0,0 +1,54 @@
import type { ModelRef } from "vue";
import type { ShoppingListItemOut, ShoppingListItemCreate } from "~/lib/api/types/household";
import { useFoodData, useFoodStore, useUnitData, useUnitStore } from "../store";
export function useShoppingListItemEditor(listItem: ModelRef<ShoppingListItemOut | ShoppingListItemCreate, string, ShoppingListItemOut | ShoppingListItemCreate, ShoppingListItemOut | ShoppingListItemCreate>) {
const foodStore = useFoodStore();
const foodData = useFoodData();
const unitStore = useUnitStore();
const unitData = useUnitData();
async function createAssignFood(val: string) {
// keep UI reactive
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
listItem.value.food ? (listItem.value.food.name = val) : (listItem.value.food = { name: val } as any);
foodData.data.name = val;
const newFood = await foodStore.actions.createOne(foodData.data);
if (newFood) {
listItem.value.food = newFood;
listItem.value.foodId = newFood.id;
}
foodData.reset();
}
async function createAssignUnit(val: string) {
// keep UI reactive
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
listItem.value.unit ? (listItem.value.unit.name = val) : (listItem.value.unit = { name: val } as any);
unitData.data.name = val;
const newUnit = await unitStore.actions.createOne(unitData.data);
if (newUnit) {
listItem.value.unit = newUnit;
listItem.value.unitId = newUnit.id;
}
unitData.reset();
}
async function assignLabelToFood() {
if (!(listItem.value.food && listItem.value.foodId && listItem.value.labelId)) {
return;
}
listItem.value.food.labelId = listItem.value.labelId;
await foodStore.actions.updateOne(listItem.value.food);
}
return {
assignLabelToFood,
createAssignFood,
createAssignUnit,
};
}

View File

@@ -5,12 +5,16 @@ import { usePublicExploreApi, useUserApi } from "~/composables/api";
const store: Ref<RecipeCategory[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
const publicLoading = ref(false);
const publicInitialized = ref(false);
export function resetCategoryStore() {
store.value = [];
loading.value = false;
initialized.value = false;
publicLoading.value = false;
publicInitialized.value = false;
}
export const useCategoryData = function () {
@@ -23,10 +27,10 @@ export const useCategoryData = function () {
export const useCategoryStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useStore<RecipeCategory>("category", store, loading, api.categories);
return useStore<RecipeCategory>("category", store, loading, initialized, api.categories);
};
export const usePublicCategoryStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeCategory>("category", store, publicLoading, api.categories);
return useReadOnlyStore<RecipeCategory>("category", store, publicLoading, publicInitialized, api.categories);
};

View File

@@ -5,17 +5,21 @@ import { usePublicExploreApi, useUserApi } from "~/composables/api";
const cookbooks: Ref<ReadCookBook[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
const publicLoading = ref(false);
const publicInitialized = ref(false);
export function resetCookbookStore() {
cookbooks.value = [];
loading.value = false;
initialized.value = false;
publicLoading.value = false;
publicInitialized.value = false;
}
export const useCookbookStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
const store = useStore<ReadCookBook>("cookbook", cookbooks, loading, api.cookbooks);
const store = useStore<ReadCookBook>("cookbook", cookbooks, loading, initialized, api.cookbooks);
const updateAll = async function (updateData: UpdateCookBook[]) {
loading.value = true;
@@ -31,5 +35,5 @@ export const useCookbookStore = function (i18n?: Composer) {
export const usePublicCookbookStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<ReadCookBook>("cookbook", cookbooks, publicLoading, api.cookbooks);
return useReadOnlyStore<ReadCookBook>("cookbook", cookbooks, publicLoading, publicInitialized, api.cookbooks);
};

View File

@@ -5,12 +5,16 @@ import { usePublicExploreApi, useUserApi } from "~/composables/api";
const store: Ref<IngredientFood[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
const publicLoading = ref(false);
const publicInitialized = ref(false);
export function resetFoodStore() {
store.value = [];
loading.value = false;
initialized.value = false;
publicLoading.value = false;
publicInitialized.value = false;
}
export const useFoodData = function () {
@@ -24,10 +28,10 @@ export const useFoodData = function () {
export const useFoodStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useStore<IngredientFood>("food", store, loading, api.foods);
return useStore<IngredientFood>("food", store, loading, initialized, api.foods);
};
export const usePublicFoodStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<IngredientFood>("food", store, publicLoading, api.foods);
return useReadOnlyStore<IngredientFood>("food", store, publicLoading, publicInitialized, api.foods);
};

View File

@@ -5,20 +5,24 @@ import { usePublicExploreApi, useUserApi } from "~/composables/api";
const store: Ref<HouseholdSummary[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
const publicLoading = ref(false);
const publicInitialized = ref(false);
export function resetHouseholdStore() {
store.value = [];
loading.value = false;
initialized.value = false;
publicLoading.value = false;
publicInitialized.value = false;
}
export const useHouseholdStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useReadOnlyStore<HouseholdSummary>("household", store, loading, api.households);
return useReadOnlyStore<HouseholdSummary>("household", store, loading, initialized, api.households);
};
export const usePublicHouseholdStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<HouseholdSummary>("household-public", store, publicLoading, api.households);
return useReadOnlyStore<HouseholdSummary>("household-public", store, publicLoading, publicInitialized, api.households);
};

View File

@@ -5,10 +5,12 @@ import { useUserApi } from "~/composables/api";
const store: Ref<MultiPurposeLabelOut[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
export function resetLabelStore() {
store.value = [];
loading.value = false;
initialized.value = false;
}
export const useLabelData = function () {
@@ -22,5 +24,5 @@ export const useLabelData = function () {
export const useLabelStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useStore<MultiPurposeLabelOut>("label", store, loading, api.multiPurposeLabels);
return useStore<MultiPurposeLabelOut>("label", store, loading, initialized, api.multiPurposeLabels);
};

View File

@@ -5,12 +5,16 @@ import { usePublicExploreApi, useUserApi } from "~/composables/api";
const store: Ref<RecipeTag[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
const publicLoading = ref(false);
const publicInitialized = ref(false);
export function resetTagStore() {
store.value = [];
loading.value = false;
initialized.value = false;
publicLoading.value = false;
publicInitialized.value = false;
}
export const useTagData = function () {
@@ -23,10 +27,10 @@ export const useTagData = function () {
export const useTagStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useStore<RecipeTag>("tag", store, loading, api.tags);
return useStore<RecipeTag>("tag", store, loading, initialized, api.tags);
};
export const usePublicTagStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeTag>("tag", store, publicLoading, api.tags);
return useReadOnlyStore<RecipeTag>("tag", store, publicLoading, publicInitialized, api.tags);
};

View File

@@ -9,12 +9,16 @@ interface RecipeToolWithOnHand extends RecipeTool {
const store: Ref<RecipeTool[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
const publicLoading = ref(false);
const publicInitialized = ref(false);
export function resetToolStore() {
store.value = [];
loading.value = false;
initialized.value = false;
publicLoading.value = false;
publicInitialized.value = false;
}
export const useToolData = function () {
@@ -29,10 +33,10 @@ export const useToolData = function () {
export const useToolStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useStore<RecipeTool>("tool", store, loading, api.tools);
return useStore<RecipeTool>("tool", store, loading, initialized, api.tools);
};
export const usePublicToolStore = function (groupSlug: string, i18n?: Composer) {
const api = usePublicExploreApi(groupSlug, i18n).explore;
return useReadOnlyStore<RecipeTool>("tool", store, publicLoading, api.tools);
return useReadOnlyStore<RecipeTool>("tool", store, publicLoading, publicInitialized, api.tools);
};

View File

@@ -5,10 +5,12 @@ import { useUserApi } from "~/composables/api";
const store: Ref<IngredientUnit[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
export function resetUnitStore() {
store.value = [];
loading.value = false;
initialized.value = false;
}
export const useUnitData = function () {
@@ -23,5 +25,5 @@ export const useUnitData = function () {
export const useUnitStore = function (i18n?: Composer) {
const api = useUserApi(i18n);
return useStore<IngredientUnit>("unit", store, loading, api.units);
return useStore<IngredientUnit>("unit", store, loading, initialized, api.units);
};

View File

@@ -6,10 +6,12 @@ import { BaseCRUDAPIReadOnly } from "~/lib/api/base/base-clients";
const store: Ref<UserSummary[]> = ref([]);
const loading = ref(false);
const initialized = ref(false);
export function resetUserStore() {
store.value = [];
loading.value = false;
initialized.value = false;
}
class GroupUserAPIReadOnly extends BaseCRUDAPIReadOnly<UserSummary> {
@@ -21,5 +23,5 @@ export const useUserStore = function (i18n?: Composer) {
const requests = useRequests(i18n);
const api = new GroupUserAPIReadOnly(requests);
return useReadOnlyStore<UserSummary>("user", store, loading, api, { orderBy: "full_name" });
return useReadOnlyStore<UserSummary>("user", store, loading, initialized, api, { orderBy: "full_name" });
};

View File

@@ -6,7 +6,12 @@ const loading = ref(false);
export const useGroupSelf = function () {
const api = useUserApi();
const auth = useMealieAuth();
async function refreshGroupSelf() {
if (!auth.user.value) {
groupSelfRef.value = null;
return;
}
loading.value = true;
const { data } = await api.groups.getCurrentUserGroup();
groupSelfRef.value = data;

View File

@@ -6,8 +6,13 @@ const loading = ref(false);
export const useHouseholdSelf = function () {
const api = useUserApi();
const auth = useMealieAuth();
async function refreshHouseholdSelf() {
if (!auth.user.value) {
householdSelfRef.value = null;
return;
}
loading.value = true;
const { data } = await api.households.getCurrentUserHousehold();
householdSelfRef.value = data;

View File

@@ -3,224 +3,224 @@ export const LOCALES = [
{
name: "繁體中文 (Chinese traditional)",
value: "zh-TW",
progress: 72,
progress: 98,
dir: "ltr",
pluralFoodHandling: "never",
},
{
name: "简体中文 (Chinese simplified)",
value: "zh-CN",
progress: 27,
progress: 54,
dir: "ltr",
pluralFoodHandling: "never",
},
{
name: "Tiếng Việt (Vietnamese)",
value: "vi-VN",
progress: 1,
progress: 2,
dir: "ltr",
pluralFoodHandling: "never",
},
{
name: "Українська (Ukrainian)",
value: "uk-UA",
progress: 60,
progress: 86,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Türkçe (Turkish)",
value: "tr-TR",
progress: 28,
progress: 54,
dir: "ltr",
pluralFoodHandling: "never",
},
{
name: "Svenska (Swedish)",
value: "sv-SE",
progress: 46,
progress: 75,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "српски (Serbian)",
value: "sr-SP",
progress: 72,
progress: 99,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Slovenščina (Slovenian)",
value: "sl-SI",
progress: 30,
progress: 57,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Slovenčina (Slovak)",
value: "sk-SK",
progress: 34,
progress: 61,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Pусский (Russian)",
value: "ru-RU",
progress: 32,
progress: 59,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Română (Romanian)",
value: "ro-RO",
progress: 33,
progress: 60,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Português (Portuguese)",
value: "pt-PT",
progress: 30,
progress: 57,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Português do Brasil (Brazilian Portuguese)",
value: "pt-BR",
progress: 72,
progress: 99,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Polski (Polish)",
value: "pl-PL",
progress: 72,
progress: 99,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Norsk (Norwegian)",
value: "no-NO",
progress: 32,
progress: 60,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Nederlands (Dutch)",
value: "nl-NL",
progress: 70,
progress: 98,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Latviešu (Latvian)",
value: "lv-LV",
progress: 27,
progress: 53,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Lietuvių (Lithuanian)",
value: "lt-LT",
progress: 22,
progress: 42,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "한국어 (Korean)",
value: "ko-KR",
progress: 28,
progress: 55,
dir: "ltr",
pluralFoodHandling: "never",
},
{
name: "日本語 (Japanese)",
value: "ja-JP",
progress: 26,
progress: 50,
dir: "ltr",
pluralFoodHandling: "never",
},
{
name: "Italiano (Italian)",
value: "it-IT",
progress: 45,
progress: 73,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Íslenska (Icelandic)",
value: "is-IS",
progress: 31,
progress: 57,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Magyar (Hungarian)",
value: "hu-HU",
progress: 34,
progress: 61,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Hrvatski (Croatian)",
value: "hr-HR",
progress: 21,
progress: 42,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "עברית (Hebrew)",
value: "he-IL",
progress: 46,
progress: 73,
dir: "rtl",
pluralFoodHandling: "always",
},
{
name: "Galego (Galician)",
value: "gl-ES",
progress: 27,
progress: 52,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Français (French)",
value: "fr-FR",
progress: 55,
progress: 82,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Français canadien (Canadian French)",
value: "fr-CA",
progress: 60,
progress: 90,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Belge (Belgian)",
value: "fr-BE",
progress: 28,
progress: 72,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Suomi (Finnish)",
value: "fi-FI",
progress: 72,
progress: 99,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Eesti (Estonian)",
value: "et-EE",
progress: 32,
progress: 58,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Español (Spanish)",
value: "es-ES",
progress: 35,
progress: 62,
dir: "ltr",
pluralFoodHandling: "always",
},
@@ -234,63 +234,63 @@ export const LOCALES = [
{
name: "British English",
value: "en-GB",
progress: 32,
progress: 36,
dir: "ltr",
pluralFoodHandling: "without-unit",
},
{
name: "Ελληνικά (Greek)",
value: "el-GR",
progress: 33,
progress: 57,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Deutsch (German)",
value: "de-DE",
progress: 72,
progress: 99,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Dansk (Danish)",
value: "da-DK",
progress: 72,
progress: 100,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Čeština (Czech)",
value: "cs-CZ",
progress: 31,
progress: 59,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Català (Catalan)",
value: "ca-ES",
progress: 33,
progress: 60,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "Български (Bulgarian)",
value: "bg-BG",
progress: 44,
progress: 71,
dir: "ltr",
pluralFoodHandling: "always",
},
{
name: "العربية (Arabic)",
value: "ar-SA",
progress: 71,
progress: 98,
dir: "rtl",
pluralFoodHandling: "always",
},
{
name: "Afrikaans (Afrikaans)",
value: "af-ZA",
progress: 18,
progress: 37,
dir: "ltr",
pluralFoodHandling: "always",
},

View File

@@ -73,11 +73,11 @@ function createRecipeExplorerSearchState(groupSlug: ComputedRef<string>): Recipe
});
// Store references
const categories = isOwnGroup ? useCategoryStore() : usePublicCategoryStore(groupSlug.value);
const foods = isOwnGroup ? useFoodStore() : usePublicFoodStore(groupSlug.value);
const households = isOwnGroup ? useHouseholdStore() : usePublicHouseholdStore(groupSlug.value);
const tags = isOwnGroup ? useTagStore() : usePublicTagStore(groupSlug.value);
const tools = isOwnGroup ? useToolStore() : usePublicToolStore(groupSlug.value);
const categories = isOwnGroup.value ? useCategoryStore() : usePublicCategoryStore(groupSlug.value);
const foods = isOwnGroup.value ? useFoodStore() : usePublicFoodStore(groupSlug.value);
const households = isOwnGroup.value ? useHouseholdStore() : usePublicHouseholdStore(groupSlug.value);
const tags = isOwnGroup.value ? useTagStore() : usePublicTagStore(groupSlug.value);
const tools = isOwnGroup.value ? useToolStore() : usePublicToolStore(groupSlug.value);
// Selected items
const selectedCategories = ref<NoUndefinedField<RecipeCategory>[]>([]);

View File

@@ -3,6 +3,11 @@ interface Toast {
text: string;
title: string | null;
color: string;
timeout?: number;
action?: {
onClick: VoidFunction;
message?: string;
};
}
export const toastAlert = reactive<Toast>({
@@ -19,11 +24,13 @@ export const toastLoading = reactive<Toast>({
color: "success",
});
function setToast(toast: Toast, text: string, title: string | null, color: string) {
function setToast(toast: Toast, text: string, title: string | null, color: string, options?: Partial<Toast>) {
toast.open = true;
toast.text = text;
toast.title = title;
toast.color = color;
toast.timeout = options?.timeout;
toast.action = options?.action;
}
export const loader = {
@@ -45,17 +52,17 @@ export const loader = {
};
export const alert = {
info(text: string, title: string | null = null) {
setToast(toastAlert, text, title, "info");
info(text: string, title: string | null = null, options?: Partial<Toast>) {
setToast(toastAlert, text, title, "info", options);
},
success(text: string, title: string | null = null) {
setToast(toastAlert, text, title, "success");
success(text: string, title: string | null = null, options?: Partial<Toast>) {
setToast(toastAlert, text, title, "success", options);
},
error(text: string, title: string | null = null) {
setToast(toastAlert, text, title, "error");
error(text: string, title: string | null = null, options?: Partial<Toast>) {
setToast(toastAlert, text, title, "error", options);
},
warning(text: string, title: string | null = null) {
setToast(toastAlert, text, title, "warning");
warning(text: string, title: string | null = null, options?: Partial<Toast>) {
setToast(toastAlert, text, title, "warning", options);
},
close() {
toastAlert.open = false;

View File

@@ -73,6 +73,10 @@ export interface UserActivityPreferences {
defaultActivity: ActivityKey;
}
export interface UserExperiencePreferences {
lockScreen: boolean;
}
export function useUserMealPlanPreferences(): Ref<UserMealPlanPreferences> {
const fromStorage = useLocalStorage(
"meal-planner-preferences",
@@ -81,9 +85,7 @@ export function useUserMealPlanPreferences(): Ref<UserMealPlanPreferences> {
numberOfDays: 7,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserMealPlanPreferences>;
);
return fromStorage;
}
@@ -92,15 +94,14 @@ export function useUserPrintPreferences(): Ref<UserPrintPreferences> {
const fromStorage = useLocalStorage(
"recipe-print-preferences",
{
imagePosition: "left",
imagePosition: "left" as ImagePosition,
showDescription: true,
showNotes: true,
showNutrition: false,
expandChildRecipes: false,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserPrintPreferences>;
);
return fromStorage;
}
@@ -118,9 +119,7 @@ export function useUserSortPreferences(): Ref<UserRecipePreferences> {
useMobileCards: false,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserRecipePreferences>;
);
return fromStorage;
}
@@ -132,9 +131,7 @@ export function useUserActivityPreferences(): Ref<UserActivityPreferences> {
defaultActivity: ActivityKey.RECIPES,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as Ref<UserActivityPreferences>;
);
return fromStorage;
}
@@ -146,9 +143,7 @@ export function useUserSearchQuerySession(): Ref<UserSearchQuery> {
recipe: "",
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserSearchQuery>;
);
return fromStorage;
}
@@ -160,9 +155,7 @@ export function useShoppingListPreferences(): Ref<UserShoppingListPreferences> {
viewAllLists: false,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserShoppingListPreferences>;
);
return fromStorage;
}
@@ -175,9 +168,7 @@ export function useTimelinePreferences(): Ref<UserTimelinePreferences> {
types: ["info", "system", "comment"] as TimelineEventType[],
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserTimelinePreferences>;
);
return fromStorage;
}
@@ -186,12 +177,10 @@ export function useParsingPreferences(): Ref<UserParsingPreferences> {
const fromStorage = useLocalStorage(
"parsing-preferences",
{
parser: "nlp",
parser: "nlp" as RegisteredParser,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserParsingPreferences>;
);
return fromStorage;
}
@@ -203,9 +192,7 @@ export function useCookbookPreferences(): Ref<UserCookbooksPreferences> {
hideOtherHouseholds: false,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserCookbooksPreferences>;
);
return fromStorage;
}
@@ -224,9 +211,7 @@ export function useRecipeFinderPreferences(): Ref<UserRecipeFinderPreferences> {
includeToolsOnHand: true,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserRecipeFinderPreferences>;
);
return fromStorage;
}
@@ -241,9 +226,19 @@ export function useRecipeCreatePreferences(): Ref<UserRecipeCreatePreferences> {
parseRecipe: true,
},
{ mergeDefaults: true },
// we cast to a Ref because by default it will return an optional type ref
// but since we pass defaults we know all properties are set.
) as unknown as Ref<UserRecipeCreatePreferences>;
);
return fromStorage;
}
export function useUserExperiencePreferences(): Ref<UserExperiencePreferences> {
const fromStorage = useLocalStorage(
"user-experience-preferences",
{
lockScreen: true,
},
{ mergeDefaults: true },
);
return fromStorage;
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Beheerpaneel",
"delete": "Verwyder",
"disabled": "Afkeskakel",
"done": "Done",
"download": "Laai af",
"duplicate": "Dupliseer",
"edit": "Wysig",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Dinsdag",
"type": "Tipe",
"undo": "Undo",
"update": "Wysig",
"updated": "Opgedateer",
"upload": "Laai op",
@@ -915,6 +917,7 @@
"quantity": "Hoeveelheid: {0}",
"shopping-list": "Inkopielys",
"shopping-lists": "Inkopielyste",
"add-item": "Add item",
"food": "Voedsel",
"note": "Nota",
"label": "Etiket",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Alle resepte",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "لوحة المعلومات",
"delete": "حذف",
"disabled": "معطَّل",
"done": "Done",
"download": "تحميل",
"duplicate": "استنساخ",
"edit": "تعديل",
@@ -168,6 +169,7 @@
"token": "الرمز التعريفي",
"tuesday": "الثلاثاء",
"type": "النوع",
"undo": "Undo",
"update": "تحديث",
"updated": "محدث",
"upload": "تحميل",
@@ -429,7 +431,7 @@
},
"myrecipebox": {
"title": "صندوق وصفاتي",
"description-long": ""
"description-long": "Mealie can import recipes from My Recipe Box. Export your recipes in CSV format, then upload the .csv file below."
},
"recipekeeper": {
"title": "مدير الوصفة",
@@ -573,11 +575,11 @@
"yield-text": "نص الإرجاع",
"quantity": "الكَمّيَّة",
"choose-unit": "اختر الوحدة",
"press-enter-to-create": "",
"press-enter-to-create": "Press Enter to Create",
"choose-food": "اختيار الطعام",
"choose-recipe": "اختر وصفة",
"notes": "ملاحظات",
"toggle-section": "",
"toggle-section": "Toggle Section",
"see-original-text": "عرض النص الأصلي",
"original-text-with-value": "النص الأصلي: {originalText}",
"ingredient-linker": "رابط المكون",
@@ -885,7 +887,7 @@
"application-version-error-text": "الإصدار الحالي الخاص بك ({0}) لا يتطابق مع الإصدار الأخير. انظر في التحديث إلى الإصدار الأحدث ({1}).",
"mealie-is-up-to-date": "Malie على آخر تحديث",
"secure-site": "موقع آمن",
"secure-site-error-text": "",
"secure-site-error-text": "Serve via localhost or secure with https. Clipboard and additional browser APIs may not work.",
"secure-site-success-text": "يتم الوصول إلى الموقع بواسطة localhost أو peps",
"server-side-base-url": "الرابط الأساسي للخادم",
"server-side-base-url-error-text": "'BASE_URL' لا يزال القيمة الافتراضية على خادم API. وهذا سيسبب مشاكل مع روابط الإشعارات التي تم إنشاؤها على الخادم لرسائل البريد الإلكتروني، إلخ.",
@@ -915,9 +917,10 @@
"quantity": "الكَمّيَّة: {0}",
"shopping-list": "قائمة التسوق",
"shopping-lists": "قوائم التسوق",
"add-item": "إضافة عنصر",
"food": "الطعام",
"note": "ملاحظة",
"label": "",
"label": "Label",
"save-label": "حفظ التصنيف",
"linked-item-warning": "هذا العنصر مرتبط بوصفة واحدة أو أكثر. تعديل الوحدات أو الأطعمة سوف يسفر عن نتائج غير متوقعة عند إضافة أو إزالة الوصفة من هذه القائمة.",
"toggle-food": "تبديل الطعام",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "هل أنت متأكد من أنك تريد تحديد جميع العناصر؟",
"are-you-sure-you-want-to-uncheck-all-items": "هل أنت متأكد من أنك تريد إلغاء تحديد جميع العناصر؟",
"are-you-sure-you-want-to-delete-checked-items": "هل أنت متأكد أنك تريد حذف جميع العناصر المحددة؟",
"no-shopping-lists-found": "لم يتم العثور على قوائم تسوق"
"no-shopping-lists-found": "لم يتم العثور على قوائم تسوق",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "جميع الوصفات",
@@ -1472,5 +1476,12 @@
"no-whitespace": "لا يسمح باستخدام المسافات",
"min-length": "يجب أن يكون على الأقل {min} أحرف",
"max-length": "يجب أن لا يتجاوز {max} حرف يجب أن يكون على الأكثر {max}أحرف "
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "وضع علامة مقروء على الكل",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Табло",
"delete": "Изтриване",
"disabled": "Изключено",
"done": "Done",
"download": "Изтегли",
"duplicate": "Дублиране",
"edit": "Редактирай",
@@ -168,6 +169,7 @@
"token": "Токен",
"tuesday": "Вторник",
"type": "Тип",
"undo": "Undo",
"update": "Актуализация",
"updated": "Последно обновени",
"upload": "Качи",
@@ -915,6 +917,7 @@
"quantity": "Количество: {0}",
"shopping-list": "Списък за пазаруване",
"shopping-lists": "Списъци за пазаруване",
"add-item": "Add item",
"food": "Продукт",
"note": "Бележка",
"label": "Етикет",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Сигурни ли сте, че искате да изберете всички елементи?",
"are-you-sure-you-want-to-uncheck-all-items": "Сигурни ли сте, че искате да премахнете отметката от всички елементи?",
"are-you-sure-you-want-to-delete-checked-items": "Сигурни ли сте, че искате да изтриете всички отметнати елементи?",
"no-shopping-lists-found": "Не са намерени списъци за пазаруване"
"no-shopping-lists-found": "Не са намерени списъци за пазаруване",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Всички рецепти",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Не са позволени интервали",
"min-length": "Трябва да съдържа поне {min} знака",
"max-length": "Трябва да бъде най-много {max} символа|Трябва да бъде най-много {max} символа"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Tauler de control",
"delete": "Suprimeix",
"disabled": "Desactivat",
"done": "Done",
"download": "Descarregar",
"duplicate": "Duplica",
"edit": "Edita",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Dimarts",
"type": "Tipus",
"undo": "Undo",
"update": "Actualitza",
"updated": "S'ha actualitzat",
"upload": "Puja",
@@ -915,6 +917,7 @@
"quantity": "Quantitat: {0}",
"shopping-list": "Llista de la compra",
"shopping-lists": "Llistes de la compra",
"add-item": "Add item",
"food": "Aliments",
"note": "Nota",
"label": "Etiqueta",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Estàs segur que vols marcar tots els elements?",
"are-you-sure-you-want-to-uncheck-all-items": "Estàs segur que vols desmarcar tots els elements?",
"are-you-sure-you-want-to-delete-checked-items": "Estàs segur que vols eliminar tots els elements marcats?",
"no-shopping-lists-found": "No s'han trobat llistes de la compra"
"no-shopping-lists-found": "No s'han trobat llistes de la compra",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Receptes",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No es permeten espais en blanc",
"min-length": "Ha de tenir almenys {min} caràcters",
"max-length": "Ha de tenir com a màxim {max} caràcter|Ha de tenir com a màxim {max} caràcters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Nástěnka",
"delete": "Smazat",
"disabled": "Deaktivováno",
"done": "Hotovo",
"download": "Stáhnout",
"duplicate": "Duplikovat",
"edit": "Upravit",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Úterý",
"type": "Typ",
"undo": "Undo",
"update": "Aktualizace",
"updated": "Aktualizováno",
"upload": "Nahrát",
@@ -179,7 +181,7 @@
"units": "Jednotky",
"back": "Zpět",
"next": "Další",
"start": "Start",
"start": "Spustit",
"toggle-view": "Přepnout zobrazení",
"date": "Datum",
"id": "Id",
@@ -331,8 +333,8 @@
"any-household": "Jakákoliv domácnost",
"no-meal-plan-defined-yet": "Dosud nebyl definován žádný jídelníček",
"no-meal-planned-for-today": "Pro dnešek není naplánováno žádné jídlo",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Počet dní v minulosti při načtení stránky",
"numberOfDaysPast-label": "Výchozí dny v minulosti",
"numberOfDays-hint": "Počet dní při načtení stránky",
"numberOfDays-label": "Výchozí dny",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Pouze recepty z těchto kategorií budou použity v jídelníčku",
@@ -442,7 +444,7 @@
"error-title": "Vypadá to, že se nám nic nepodařilo najít",
"from-url": "Přenést recept",
"github-issues": "Hlášení chyb na GitHubu",
"google-ld-json-info": "Google ld+json Info",
"google-ld-json-info": "Informace o Google ld+json",
"must-be-a-valid-url": "Musí být validní URL",
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Vložte data receptu. Každý řádek bude považován za položku v seznamu",
"recipe-markup-specification": "Specifikace Markupu pro recept",
@@ -480,7 +482,7 @@
"recipe": {
"add-key": "Přidat klíč",
"add-to-favorites": "Přidat do oblíbených",
"api-extras": "API Extras",
"api-extras": "API doplňky",
"calories": "Kalorie",
"calories-suffix": "kalorie",
"carbohydrate-content": "Sacharidy",
@@ -638,8 +640,8 @@
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Vytvořte recept zadáním názvu. Všechny recepty musí mít jedinečná jména.",
"new-recipe-names-must-be-unique": "Názvy receptů musí být jedinečné",
"scrape-recipe": "Zpracovat recept",
"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-description-transcription": "You can also provide the url to a video and Mealie will attempt to transcribe it into a recipe.",
"scrape-recipe-description": "Stáhněte recept z URL. Zadejte URL webu, ze kterého chcete recept stáhnout, a Mealie se pokusí recept stáhnout a přidat ho do vaší kolekce.",
"scrape-recipe-description-transcription": "Můžete také zadat URL videa a Mealie se pokusí přepsat ho do receptu.",
"scrape-recipe-have-a-lot-of-recipes": "Máte spoustu receptů, které chcete zpracovat najednou?",
"scrape-recipe-suggest-bulk-importer": "Vyzkoušejte hromadný import",
"scrape-recipe-have-raw-html-or-json-data": "Máte surová data HTML nebo JSON?",
@@ -655,7 +657,7 @@
"import-from-html-or-json": "Importovat z HTML nebo JSON",
"import-from-html-or-json-description": "Import jednoho receptu ze surového HTML nebo JSON. To je užitečné, pokud máte recept z webu, který Mealie nedokáže normálně seškrábat, nebo z jiného externího zdroje.",
"json-import-format-description-colon": "Chcete-li importovat přes JSON, musí mít platný formát:",
"json-editor": "JSON Editor",
"json-editor": "JSON editor",
"zip-files-must-have-been-exported-from-mealie": "Soubory .zip musí být exportovány z aplikace Mealie",
"create-a-recipe-by-uploading-a-scan": "Vytvořte recept nahráním skenu.",
"upload-a-png-image-from-a-recipe-book": "Nahrát png obrázek z knihy receptů",
@@ -835,7 +837,7 @@
},
"token": {
"active-tokens": "AKTIVNÍ TOKENY",
"api-token": "API Token",
"api-token": "API token",
"api-tokens": "API Tokeny",
"copy-this-token-for-use-with-an-external-application-this-token-will-not-be-viewable-again": "Zkopírujte tento token pro použití v externí aplikaci. Tento token nebude znovu zobrazen.",
"create-an-api-token": "Vytvořit nový API token",
@@ -865,7 +867,7 @@
},
"bug-report": "Chybové hlášení",
"bug-report-information": "Použijte tyto informace k nahlášení chyby. Poskytnutí podrobností vaší instance vývojářům je nejlepší způsob, jak rychle vyřešit vaše problémy.",
"tracker": "Tracker",
"tracker": "Sledovač",
"configuration": "Konfigurace",
"docker-volume": "Volume dockeru",
"docker-volume-help": "Mealie vyžaduje, aby kontejner prostředí a podpůrné vrstvy sdílely stejný úložný prostor dockeru. Tím se zajistí, že kontejner prostředí bude moci správně přistupovat k obrázkům a informacím uloženým na disku.",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "`BASE_URL` je stále výchozí hodnotou na serveru API. To způsobí problémy s odkazy v oznámení generované na serveru pro e-maily atd.",
"server-side-base-url-success-text": "Adresa URL na straně serveru neodpovídá výchozímu nastavení",
"ldap-ready": "LDAP připraven",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP nepřipraveno",
"ldap-ready-error-text": "Nejsou nakonfigurovány všechny LDAP hodnoty. To můžete ignorovat, pokud nepoužíváte LDAP autentizaci.",
"ldap-ready-success-text": "Všechny požadované proměnné LDAP jsou nastaveny.",
"build": "Sestavení",
"recipe-scraper-version": "Verze scraperu receptů",
"oidc-ready": "OIDC připraveno",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC nepřipraveno",
"oidc-ready-error-text": "Nejsou nakonfigurovány všechny OIDC hodnoty. To můžete ignorovat, pokud nepoužíváte OIDC autentizaci.",
"oidc-ready-success-text": "Všechny požadované proměnné OIDC jsou nastaveny.",
"openai-ready": "OpenAI připraveno",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI nepřipraveno",
"openai-ready-error-text": "Nejsou nakonfigurovány všechny OpenAI hodnoty. To můžete ignorovat, pokud nepoužíváte OpenAI funkce.",
"openai-ready-success-text": "Všechny požadované proměnné OpenAI jsou nastaveny."
},
@@ -909,12 +911,13 @@
"all-lists": "Všechny seznamy",
"create-shopping-list": "Vytvořit nákupní seznam",
"from-recipe": "Z receptu",
"ingredient-of-recipe": "Ingredient of {recipe}",
"ingredient-of-recipe": "Ingredience receptu {recipe}",
"list-name": "Název seznamu",
"new-list": "Nový seznam",
"quantity": "Množství: {0}",
"shopping-list": "Nákupní seznam",
"shopping-lists": "Nákupní seznamy",
"add-item": "Add item",
"food": "Jídlo",
"note": "Poznámka",
"label": "Popisek",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Opravdu chcete vybrat všechny položky?",
"are-you-sure-you-want-to-uncheck-all-items": "Opravdu chcete zrušit výběr všech položek?",
"are-you-sure-you-want-to-delete-checked-items": "Opravdu chcete odstranit všechny vybrané položky?",
"no-shopping-lists-found": "Nebyly nalezeny žádné nákupní seznamy"
"no-shopping-lists-found": "Nebyly nalezeny žádné nákupní seznamy",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Všechny recepty",
@@ -1010,7 +1014,7 @@
"current-password": "Současné heslo",
"e-mail-must-be-valid": "E-mail musí být platný",
"edit-user": "Upravit uživatele",
"email": "Email",
"email": "E-mail",
"error-cannot-delete-super-user": "Chyba! Nelze odstranit superuživatele",
"existing-password-does-not-match": "Hesla se neshodují",
"full-name": "Jméno a příjmení",
@@ -1142,18 +1146,18 @@
"example-unit-plural": "např.: Čajové lžičky",
"example-unit-abbreviation-singular": "např.: čl",
"example-unit-abbreviation-plural": "např.: čl",
"standardization": "Standardization",
"standardization-description": "How this unit can be represented as a standard unit. This enables unit conversion features such as merging compatible units in shopping lists.",
"standard-unit": "Standard Unit",
"standard-quantity": "Standard Quantity",
"unit-conversion": "Unit Conversion",
"standardization": "Standardizace",
"standardization-description": "Jak lze tuto jednotku reprezentovat jako standardní jednotku. Umožňuje funkce konverze jednotek, jako je slučování kompatibilních jednotek v nákupních seznamech.",
"standard-unit": "Standardní jednotka",
"standard-quantity": "Standardní množství",
"unit-conversion": "Konverze jednotek",
"standard-unit-labels": {
"fluid-ounce": "fluid ounce",
"cup": "cup",
"ounce": "ounce",
"pound": "pound",
"milliliter": "milliliter",
"liter": "liter",
"fluid-ounce": "tekutá unce",
"cup": "šálek",
"ounce": "unce",
"pound": "libra",
"milliliter": "mililitr",
"liter": "litr",
"gram": "gram",
"kilogram": "kilogram"
}
@@ -1192,13 +1196,13 @@
"edit-recipe-action": "Upravit akci receptu",
"action-type": "Typ akce",
"action-types": {
"link": "Link",
"link": "Odkaz",
"post": "Publikovat"
}
},
"create-alias": "Vytvořit alias",
"manage-aliases": "Spravovat aliasy",
"seed-data": "Seed Data",
"seed-data": "Výchozí data",
"seed": "Zdroj",
"data-management": "Správa dat",
"data-management-description": "Vyberte datovou sadu, ve které chcete provést změny.",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Mezery nejsou povoleny",
"min-length": "Musí být alespoň {min} znaků",
"max-length": "Musí být maximálně {max} znak|Musí být na většině {max} znaků"
},
"announcements": {
"announcements": "Oznámení",
"all-announcements": "Všechna oznámení",
"mark-all-as-read": "Označit vše jako přečtené",
"show-announcements-from-mealie": "Zobrazit oznámení od Mealie",
"show-announcements-setting-description": "Pokud je povoleno, uživatelé uvidí oznámení od Mealie. Mohou to změnit ve svém uživatelském nastavení"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Overblik",
"delete": "Slet",
"disabled": "Deaktiveret",
"done": "Udført",
"download": "Hent",
"duplicate": "Kopier",
"edit": "Rediger",
@@ -168,6 +169,7 @@
"token": "Nøgle",
"tuesday": "Tirsdag",
"type": "Type",
"undo": "Fortryd",
"update": "Gem",
"updated": "Ændret",
"upload": "Upload",
@@ -182,7 +184,7 @@
"start": "Start",
"toggle-view": "Skift visning",
"date": "Dato",
"id": "Id",
"id": "ID",
"owner": "Ejer",
"change-owner": "Skift ejer",
"date-added": "Oprettelsesdato",
@@ -686,9 +688,9 @@
"explanation": "For at bruge ingrediensfortolkeren, skal du klikke på knappen 'Fortolk alt' for at starte behandlingen. Når relevante ingredienser er identificeret, kan du gennemgå dem og kontrollere, at de blev korrekt identificeret. Modellens konfidensscore vises til højre for ingrediensens titel. Denne score er et gennemsnit af alle de enkelte scorer og er måske ikke altid helt præcis.",
"alerts-explainer": "En advarsel vil blive vist, hvis en identificeret fødevare eller måleenhed ikke findes i databasen.",
"select-parser": "Vælg fortolker",
"natural-language-processor": "Natural Language Processor",
"brute-parser": "Brute Parser",
"openai-parser": "OpenAI Parser",
"natural-language-processor": "Naturlig sprogbehandler",
"brute-parser": "Rå parser",
"openai-parser": "OpenAI-parser",
"parse-all": "Fortolk alt",
"no-unit": "Ingen enhed",
"missing-unit": "Opret manglende måleenhed: {unit}",
@@ -835,8 +837,8 @@
},
"token": {
"active-tokens": "Aktive tokens",
"api-token": "API Token",
"api-tokens": "API Tokens",
"api-token": "API-token",
"api-tokens": "API-tokener",
"copy-this-token-for-use-with-an-external-application-this-token-will-not-be-viewable-again": "Kopier denne token ved brug i en anden applikation. Denne token kan ikke ses igen.",
"create-an-api-token": "Opret en API token",
"token-name": "Tokennavn",
@@ -855,9 +857,9 @@
"unorganized": "Uorganiseret"
},
"webhooks": {
"test-webhooks": "Test Webhooks",
"test-webhooks": "Test webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Webadresserne, der er anført nedenfor, modtager webhooks, der indeholder opskriftsdataene for måltidsplanen på den planlagte dag. \nWebhooks udføres i øjeblikket på ",
"webhook-url": "Webhook URL",
"webhook-url": "Webhook-URL",
"webhooks-caps": "WEBHOOKS",
"webhooks": "Webhooks",
"webhook-name": "Webhooknavn",
@@ -865,9 +867,9 @@
},
"bug-report": "Fejlrapport",
"bug-report-information": "Brug denne information til at rapportere en fejl. At give detaljer om din instans til udviklere er den bedste måde at få dine problemer løst hurtigt.",
"tracker": "Tracker",
"tracker": "Sporing",
"configuration": "Konfiguration",
"docker-volume": "Docker Volume",
"docker-volume": "Docker-volumen",
"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": "Docker mapper er forkert konfigureret.",
"volumes-are-configured-correctly": "Docker mapper er korrekt konfigureret.",
@@ -915,6 +917,7 @@
"quantity": "Antal: {0}",
"shopping-list": "Indkøbsliste",
"shopping-lists": "Indkøbslister",
"add-item": "Tilføj vare",
"food": "Fødevarer",
"note": "Note",
"label": "Etiket",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Er du sikker på, at du vil markere alle elementer?",
"are-you-sure-you-want-to-uncheck-all-items": "Er du sikker på, at du vil fjerne markeringen af alle elementer?",
"are-you-sure-you-want-to-delete-checked-items": "Er du sikker på, at du vil sletter de valgte elementer?",
"no-shopping-lists-found": "Ingen Indkøbslister fundet"
"no-shopping-lists-found": "Ingen Indkøbslister fundet",
"item-checked-off": "Krydsede {item} af"
},
"sidebar": {
"all-recipes": "Alle opskrifter",
@@ -953,7 +957,7 @@
"profile": "Profil",
"search": "Søg",
"site-settings": "Sideindstillinger",
"tags": "Tags",
"tags": "Etiketter",
"toolbox": "Værktøjskasse",
"language": "Sprog",
"maintenance": "Vedligeholdelse",
@@ -980,7 +984,7 @@
"tag-deletion-failed": "Sletning af tag fejlede",
"tag-update-failed": "Opdatering af tag fejlede",
"tag-updated": "Tag blev opdateret",
"tags": "Tags",
"tags": "Etiket",
"untagged-count": "Ikke-tagget: {count}",
"create-a-tag": "Opret tag",
"tag-name": "Tag navn",
@@ -1016,7 +1020,7 @@
"full-name": "Fulde navn",
"generate-password-reset-link": "Generér link til nulstilling af adgangskode",
"invite-only": "Kun inviterede",
"link-id": "Link ID",
"link-id": "Link-ID",
"link-name": "Linknavn",
"login": "Log på",
"login-oidc": "Log ind med",
@@ -1329,7 +1333,7 @@
"action-clean-images-description": "Fjerner alle de billeder, der ikke slutter med .webp",
"actions-description": "Vedligeholdelseshandlinger er {destructive_in_bold} og bør bruges med forsigtighed. Udførelse af alle disse handlinger er {irreversible_in_bold}.",
"actions-description-destructive": "destruktive",
"actions-description-irreversible": "irreversible",
"actions-description-irreversible": "uomgørlig",
"logs-action-refresh": "Opdater logfiler",
"logs-page-title": "Mealie logfiler",
"logs-tail-lines-label": "Følg log"
@@ -1341,7 +1345,7 @@
"ingredients-natural-language-processor-explanation": "Mealie bruger Conditional Random Fields felter (CRF'er) til berarbejdning af ingredienser. Den model, der anvendes til ingredienser er baseret ud fra et datasæt på over 100.000 ingredienser fra et datasæt udarbejdet af New York Times. Bemærk, at da modellen kun er trænet på engelsk, kan du have forskellige resultater, når du bruger modellen på andre sprog. På denne side kan du teste modellen.",
"ingredients-natural-language-processor-explanation-2": "Det er ikke perfekt, men giver generelt gode resultater og er et godt udgangspunkt for manuel redigering af ingredienser i individuelle felter. Alternativt kan du også bruge \"Brute\"-metoden, der bruger en mønstermatchende teknik til at identificere ingredienser.",
"nlp": "NLP",
"brute": "Brute",
"brute": "",
"openai": "OpenAI",
"show-individual-confidence": "Vis individual konfidensscore",
"ingredient-text": "Ingredienstekst",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Mellemrum er ikke tilladt",
"min-length": "Der skal mindst være {min} tegn",
"max-length": "Må højst være {max} tegn|Må højst være {max} tegn"
},
"announcements": {
"announcements": "Meddelelser",
"all-announcements": "Alle meddelelser",
"mark-all-as-read": "Markér alle som læst",
"show-announcements-from-mealie": "Vis meddelelser fra Mealie",
"show-announcements-setting-description": "Hvorvidt det ønskes, at brugere skal kunne se meddelelser fra Mealie. Hvis dette er slået til, er det stadig muligt for brugere at fravælge det i deres brugerindstillinger."
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Übersicht",
"delete": "Löschen",
"disabled": "Deaktiviert",
"done": "Done",
"download": "Herunterladen",
"duplicate": "Duplizieren",
"edit": "Bearbeiten",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Dienstag",
"type": "Typ",
"undo": "Undo",
"update": "Aktualisieren",
"updated": "Aktualisiert",
"upload": "Hochladen",
@@ -915,6 +917,7 @@
"quantity": "Menge: {0}",
"shopping-list": "Einkaufsliste",
"shopping-lists": "Einkaufslisten",
"add-item": "Add item",
"food": "Lebensmittel",
"note": "Notiz",
"label": "Kategorie",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Bist du sicher, dass du alle Elemente markieren möchtest?",
"are-you-sure-you-want-to-uncheck-all-items": "Bist du sicher, dass du die Auswahl aller Elemente aufheben möchtest?",
"are-you-sure-you-want-to-delete-checked-items": "Bist du sicher, dass du alle ausgewählten Elemente löschen möchtest?",
"no-shopping-lists-found": "Keine Einkaufslisten gefunden"
"no-shopping-lists-found": "Keine Einkaufslisten gefunden",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Alle Rezepte",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Kein Leerzeichen erlaubt",
"min-length": "Muss mindestens {min} Zeichen haben",
"max-length": "Muss maximal {max} Zeichen haben|Muss maximal {max} Zeichen haben"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Alle als gelesen markieren",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Ταμπλό",
"delete": "Διαγραφή",
"disabled": "Ανενεργό",
"done": "Ολοκληρώθηκε",
"download": "Λήψη",
"duplicate": "Δημιουργία διπλότυπου",
"edit": "Επεξεργασία",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Τρίτη",
"type": "Τύπος",
"undo": "Undo",
"update": "Ενημέρωση",
"updated": "Ενημερώθηκε",
"upload": "Ανέβασμα",
@@ -915,6 +917,7 @@
"quantity": "Ποσότητα: {0}",
"shopping-list": "Λίστα για ψώνια",
"shopping-lists": "Λίστες για ψώνια",
"add-item": "Add item",
"food": "Τρόφιμο",
"note": "Σημείωση",
"label": "Ετικέτα",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Θέλετε σίγουρα να επιλέξετε όλα τα αντικείμενα;",
"are-you-sure-you-want-to-uncheck-all-items": "Θέλετε σίγουρα να αποεπιλέξετε όλα τα αντικείμενα;",
"are-you-sure-you-want-to-delete-checked-items": "Θέλετε σίγουρα να διαγράψετε όλα τα επιλεγμένα αντικείμενα;",
"no-shopping-lists-found": "Δεν βρέθηκαν λίστες για ψώνια"
"no-shopping-lists-found": "Δεν βρέθηκαν λίστες για ψώνια",
"item-checked-off": "Το {item} επισημάνθηκε ως ολοκληρωμένο"
},
"sidebar": {
"all-recipes": "Συνταγές όλες",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Δεν επιτρέπονται κενοί χαρακτήρες",
"min-length": "Πρέπει να αποτελείται από τουλάχιστον {min} χαρακτήρες",
"max-length": "Πρέπει να αποτελείται το πολύ από {max} χαρακτήρα|Πρέπει να αποτελείται το πολύ από {max} χαρακτήρες"
},
"announcements": {
"announcements": "Ανακοινώσεις",
"all-announcements": "Oλες οι ανακοινώσεις",
"mark-all-as-read": "Σήμανση όλων ως αναγνωσμένων",
"show-announcements-from-mealie": "Εμφάνιση ανακοινώσεων από το Mealie",
"show-announcements-setting-description": "Εάνε θέλετε ή όχι να επιτρέψετε στους χρήστες να βλέπουν ανακοινώσεις από την Mealie. Οταν είναι ενεργοποιημένο οι χρήστες μπορούν ακόμα να απεγγραφούν από αυτές από τις ρυθμίσεις χρήστη τους ώστε να μην τις βλέπουν"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Dashboard",
"delete": "Delete",
"disabled": "Disabled",
"done": "Done",
"download": "Download",
"duplicate": "Duplicate",
"edit": "Edit",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Tuesday",
"type": "Type",
"undo": "Undo",
"update": "Update",
"updated": "Updated",
"upload": "Upload",
@@ -261,7 +263,7 @@
"enable-public-access": "Enable Public Access",
"enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in",
"allow-users-outside-of-your-group-to-see-your-recipes": "Allow users outside 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",
"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 authorising 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-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 now be shown",
"show-recipe-assets": "Show recipe assets",
@@ -270,7 +272,7 @@
"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-organizing-recipe-ingredients-by-units-and-food": "Disable organizing recipe ingredients by units and food",
"disable-organizing-recipe-ingredients-by-units-and-food": "Disable organising 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",
@@ -915,6 +917,7 @@
"quantity": "Quantity: {0}",
"shopping-list": "Shopping List",
"shopping-lists": "Shopping Lists",
"add-item": "Add item",
"food": "Food",
"note": "Note",
"label": "Label",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "All Recipes",
@@ -1152,8 +1156,8 @@
"cup": "cup",
"ounce": "ounce",
"pound": "pound",
"milliliter": "milliliter",
"liter": "liter",
"milliliter": "millilitre",
"liter": "litre",
"gram": "gram",
"kilogram": "kilogram"
}
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -169,6 +169,7 @@
"token": "Token",
"tuesday": "Tuesday",
"type": "Type",
"undo": "Undo",
"update": "Update",
"updated": "Updated",
"upload": "Upload",
@@ -916,6 +917,7 @@
"quantity": "Quantity: {0}",
"shopping-list": "Shopping List",
"shopping-lists": "Shopping Lists",
"add-item": "Add item",
"food": "Food",
"note": "Note",
"label": "Label",
@@ -940,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "All Recipes",

View File

@@ -98,6 +98,7 @@
"dashboard": "Panel de control",
"delete": "Eliminar",
"disabled": "Deshabilitado",
"done": "Done",
"download": "Descargar",
"duplicate": "Duplicar",
"edit": "Editar",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Martes",
"type": "Tipo",
"undo": "Undo",
"update": "Actualizar",
"updated": "Actualizado",
"upload": "Subir",
@@ -915,6 +917,7 @@
"quantity": "Cantidad: {0}",
"shopping-list": "Lista de la compra",
"shopping-lists": "Listas de la compra",
"add-item": "Add item",
"food": "Alimentos",
"note": "Nota",
"label": "Etiqueta",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "¿Seguro que quieres seleccionar todos los elementos?",
"are-you-sure-you-want-to-uncheck-all-items": "¿Seguro que quieres de-seleccionar todos los elementos?",
"are-you-sure-you-want-to-delete-checked-items": "¿Está seguro que deseas eliminar los elementos seleccionados?",
"no-shopping-lists-found": "No hay listas de la compra"
"no-shopping-lists-found": "No hay listas de la compra",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Recetas",
@@ -1462,7 +1466,7 @@
"is-not-like": "no es como"
},
"dates": {
"days-ago": ""
"days-ago": "days ago|day ago|days ago"
}
},
"validators": {
@@ -1472,5 +1476,12 @@
"no-whitespace": "No se permiten espacios en blanco",
"min-length": "Debe tener como mínimo {min} caracteres",
"max-length": "Debe Ser Como Máximo {max} Carácter|Debe Ser Como Máximo {max} Caracteres"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Töölaud",
"delete": "Kustuta",
"disabled": "Keelatud",
"done": "Done",
"download": "Lae alla",
"duplicate": "Duplitseeri",
"edit": "Muuda",
@@ -168,6 +169,7 @@
"token": "Identifikaator",
"tuesday": "Teisipäev",
"type": "Tüüp",
"undo": "Undo",
"update": "Uuenda",
"updated": "Uuendatud",
"upload": "Lae üles",
@@ -569,7 +571,7 @@
"failed-to-add-recipe-to-mealplan": "Retsepti lisamine toitumisplaani ebaõnnestus",
"failed-to-add-to-list": "Nimekirja lisamine ebaõnnestus",
"yield": "Saagikus",
"yields-amount-with-text": "",
"yields-amount-with-text": "Yields {amount} {text}",
"yield-text": "Saaduse tekst",
"quantity": "Kogus",
"choose-unit": "Vali ühik",
@@ -915,6 +917,7 @@
"quantity": "Kogus: {0}",
"shopping-list": "Ostunimekiri",
"shopping-lists": "Ostunimekirjad",
"add-item": "Add item",
"food": "Toit",
"note": "Märkus",
"label": "Silt",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Kas oled kindel, et tahad valida kõik üksused?",
"are-you-sure-you-want-to-uncheck-all-items": "Kas oled kindel, et tahad tühistada kõik valikud?",
"are-you-sure-you-want-to-delete-checked-items": "Kas oled kindel, et tahad kustutada kõik valitud üksused?",
"no-shopping-lists-found": "Poenimekirja ei leitud"
"no-shopping-lists-found": "Poenimekirja ei leitud",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Kõik retseptid",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -51,7 +51,7 @@
"category": "Kategoria"
},
"events": {
"apprise-url": "Apprise URL",
"apprise-url": "Apprise-url",
"database": "Tietokanta",
"delete-event": "Poista tapahtuma",
"event-delete-confirmation": "Haluatko varmasti poistaa tämän tapahtuman?",
@@ -98,6 +98,7 @@
"dashboard": "Hallintanäkymä",
"delete": "Poista",
"disabled": "Poistettu käytöstä",
"done": "Valmis",
"download": "Lataa",
"duplicate": "Monista",
"edit": "Muokkaa",
@@ -168,6 +169,7 @@
"token": "Tunniste",
"tuesday": "Tiistai",
"type": "Tyyppi",
"undo": "Peru",
"update": "Päivitä",
"updated": "Päivitetty",
"upload": "Lähetä",
@@ -331,8 +333,8 @@
"any-household": "Mikä tahansa kotitalous",
"no-meal-plan-defined-yet": "Ateriasuunnitelmaa ei ole vielä määritelty",
"no-meal-planned-for-today": "Ei ateriasuunnitelmaa tälle päivälle",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Menneisyydestä ladattujen päivien määrä",
"numberOfDaysPast-label": "Oletusarvo menneiden päivien lataukselle",
"numberOfDays-hint": "Sivun latauspäivien lukumäärä",
"numberOfDays-label": "Oletuspäivät",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Vain näiden luokkien reseptejä käytetään ateriasuunnitelmissa",
@@ -390,7 +392,7 @@
"nextcloud": {
"description": "Tuo tiedot Nextcloudin Cookbookista",
"description-long": "Nextcloud reseptejä voidaan tuoda zip-tiedostosta, joka sisältää Nextcloudin tallennetut tiedot. Katso esimerkkikansiorakenne alla varmistaaksesi, että reseptisi voidaan tuoda.",
"title": "Nextcloud Cookbook"
"title": "Nextcloud-keittokirja"
},
"copymethat": {
"description-long": "Mealie voi tuoda reseptejä Copy Me That -sovelluksesta. Vie reseptisi HTML-muodossa ja lataa sitten zip-tiedosto.",
@@ -700,7 +702,7 @@
"confidence-score": "Varmuuspisteet",
"ingredient-parser-description": "Ainesosat on haettu onnistuneesti. Ole hyvä ja tarkista ainesosat joista emme ole varmoja.",
"ingredient-parser-final-review-description": "Kun kaikki ainesosat on tarkistettu, sinulla on vielä yksi mahdollisuus tarkistaa kaikki ainesosat ennen kuin muokkaat reseptiäsi.",
"add-text-as-alias-for-item": "",
"add-text-as-alias-for-item": "Lisää \"{text}\" kohteen {item} aliakseksi",
"delete-item": "Poista kohde"
},
"reset-servings-count": "Palauta Annoksien Määrä",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "`BASE_URL` on API-palvelimen oletusarvo. Tämä aiheuttaa ongelmia ilmoitusten linkkien kanssa, jotka on luotu palvelimella sähköposteja varten jne.",
"server-side-base-url-success-text": "Palvelimen nettiosoite ei vastaa oletusta",
"ldap-ready": "LDAP Valmis",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP ei valmis",
"ldap-ready-error-text": "Kaikkia LDAP-arvoja ei ole määritetty. Tämä voidaan ohittaa, jos et käytä LDAP-todennusta.",
"ldap-ready-success-text": "Kaikki vaaditut LDAP-muuttujat on asetettu.",
"build": "Koonti",
"recipe-scraper-version": "Reseptikaappaimen versio",
"oidc-ready": "OIDC valmis",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC ei ole valmis",
"oidc-ready-error-text": "Kaikkia OIDC-arvoja ei ole määritelty. Jos et käytä OIDC-todennusta, voidaan asia jättää huomiotta.",
"oidc-ready-success-text": "Kaikki vaaditut OIDC-muuttujat asetettu.",
"openai-ready": "OpenAI valmis",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI ei ole valmis",
"openai-ready-error-text": "Kaikkia OpenAI:n arvoja ei ole määritelty. Tämä voidaan sivuuttaa, mikäli et käytä OpenAI:n toimintoja.",
"openai-ready-success-text": "Vaadittavat OpenAI-muuttujat ovat asetetut."
},
@@ -915,6 +917,7 @@
"quantity": "Määrä: {0}",
"shopping-list": "Ostoslista",
"shopping-lists": "Ostoslistat",
"add-item": "Lisää kohde",
"food": "Elintarvikkeet",
"note": "Muistiinpano",
"label": "Tunnus",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Haluatko varmasti valita kaikki kohteet?",
"are-you-sure-you-want-to-uncheck-all-items": "Haluatko varmasti poistaa kaikki valinnat?",
"are-you-sure-you-want-to-delete-checked-items": "Haluatko varmasti poistaa kaikki valitut kohteet?",
"no-shopping-lists-found": "Ostoslistoja ei löytynyt"
"no-shopping-lists-found": "Ostoslistoja ei löytynyt",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Reseptit",
@@ -958,7 +962,7 @@
"language": "Kieli",
"maintenance": "Ylläpito",
"background-tasks": "Taustatehtävät",
"parser": "Parser",
"parser": "Jäsentäjä",
"developer": "Kehittäjä",
"cookbook": "Keittokirja",
"create-cookbook": "Luo uusi keittokirja"
@@ -1347,7 +1351,7 @@
"ingredient-text": "Ainesosan Teksti",
"average-confident": "{0} Luottamus",
"try-an-example": "Kokeile esimerkkiä",
"parser": "Parser",
"parser": "Jäsentäjä",
"background-tasks": "Taustatehtävät",
"background-tasks-description": "Täältä voit tarkastella kaikkia käynnissä olevia taustatehtäviä ja niiden tilaa",
"no-logs-found": "Lokeja Ei Löytynyt",
@@ -1372,7 +1376,7 @@
"profile": {
"welcome-user": "👋 Tervetuloa, {0}!",
"description": "Hallitse profiiliasi, reseptejäsi ja ryhmäasetuksiasi.",
"invite-link": "",
"invite-link": "Invite Link",
"get-invite-link": "Hanki Kutsulinkki",
"get-public-link": "Julkinen linkki",
"account-summary": "Tilin Yhteenveto",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Tekstissä ei saa olla välilyöntejä",
"min-length": "Vähimmäispituus on {min} merkkiä",
"max-length": "Saa olla enintään {max} merkki|Saa olla enintään {max} merkkiä"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Näytä Mealien ilmoitukset",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Tableau de bord",
"delete": "Supprimer",
"disabled": "Désactivé",
"done": "Terminé",
"download": "Télécharger",
"duplicate": "Dupliquer",
"edit": "Modifier",
@@ -168,6 +169,7 @@
"token": "Jeton",
"tuesday": "Mardi",
"type": "Type",
"undo": "Undo",
"update": "Mettre à jour",
"updated": "Mis à jour",
"upload": "Importer",
@@ -331,8 +333,8 @@
"any-household": "Tout foyer",
"no-meal-plan-defined-yet": "Aucun menu planifié",
"no-meal-planned-for-today": "Aucun repas prévu pour aujourdhui",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Nombre de jours passé à charger",
"numberOfDaysPast-label": "Jours par défaut dans le pas",
"numberOfDays-hint": "Nombre de jours lors du chargement de la page",
"numberOfDays-label": "Jours par défaut",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Seules les recettes appartenant à ces catégories seront utilisées dans les menus",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "`BASE_URL` est encore la valeur par défaut sur le serveur API. Cela causera des problèmes avec les liens générés par les notifications sur le serveur pour les e-mails, etc.",
"server-side-base-url-success-text": "L'URL du côté du serveur ne correspond pas à celle par défaut",
"ldap-ready": "Prêt pour LDAP",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP n'est pas prêt",
"ldap-ready-error-text": "Toutes les valeurs LDAP ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous n'utilisez pas l'authentification LDAP.",
"ldap-ready-success-text": "Les variables LDAP obligatoires sont toutes définies.",
"build": "Build",
"recipe-scraper-version": "Version du Scraper de recette",
"oidc-ready": "Prêt pour OIDC",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC n'est pas prêt",
"oidc-ready-error-text": "Toutes les valeurs OIDC ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous nutilisez pas lauthentification OIDC.",
"oidc-ready-success-text": "Les variables OIDC obligatoires sont toutes définies.",
"openai-ready": "Prêt pour OpenAI",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI n'est pas prêt",
"openai-ready-error-text": "Toutes les valeurs OpenAI ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous n'utilisez pas les fonctionnalités OpenAI.",
"openai-ready-success-text": "Les variables OpenAI obligatoires sont toutes définies."
},
@@ -915,8 +917,9 @@
"quantity": "Quantité: {0}",
"shopping-list": "Liste de courses",
"shopping-lists": "Listes de courses",
"add-item": "Ajouter un article",
"food": "Aliment",
"note": "Note",
"note": "Remarque",
"label": "Étiquette",
"save-label": "Sauvegarder le libellé",
"linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Voulez-vous vraiment sélectionner tous les éléments ?",
"are-you-sure-you-want-to-uncheck-all-items": "Voulez-vous vraiment désélectionner tous les éléments ?",
"are-you-sure-you-want-to-delete-checked-items": "Voulez-vous vraiment supprimer tous les éléments sélectionnés ?",
"no-shopping-lists-found": "Aucune liste de courses trouvée"
"no-shopping-lists-found": "Aucune liste de courses trouvée",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Recettes",
@@ -1347,7 +1351,7 @@
"ingredient-text": "Texte de l'ingrédient",
"average-confident": "Confiant à {0}",
"try-an-example": "Essayez avec un exemple",
"parser": "Parser",
"parser": "Analyseur syntaxique",
"background-tasks": "Tâches en arrière plan",
"background-tasks-description": "Ici vous pouvez voir toutes les tâches en arrière-plan en cours et leur statut",
"no-logs-found": "Pas de journaux trouvés",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Aucun espace n'est autorisé",
"min-length": "Doit contenir au moins {min} caractères",
"max-length": "Doit comporter au maximum {max} caractère|Doit comporter au maximum {max} caractères"
},
"announcements": {
"announcements": "Annonces",
"all-announcements": "Toutes les annonces",
"mark-all-as-read": "Marquer tout comme lu",
"show-announcements-from-mealie": "Afficher les annonces de Mealie",
"show-announcements-setting-description": "Si vous voulez autoriser ou non les utilisateurs à voir les annonces de Mealie. Lorsque cette option est activée, les utilisateurs peuvent toujours refuser de les voir dans leurs paramètres utilisateur"
}
}

View File

@@ -19,7 +19,7 @@
"log-lines": "Lignes du journal",
"not-demo": "Non démo",
"portfolio": "Portfolio",
"production": "Production",
"production": "Réalisation",
"support": "Soutenir",
"version": "Version",
"unknown-version": "inconnu",
@@ -98,6 +98,7 @@
"dashboard": "Tableau de bord",
"delete": "Supprimer",
"disabled": "Désactivé",
"done": "Terminé",
"download": "Télécharger",
"duplicate": "Dupliquer",
"edit": "Modifier",
@@ -168,6 +169,7 @@
"token": "Jeton",
"tuesday": "Mardi",
"type": "Type",
"undo": "Undo",
"update": "Mettre à jour",
"updated": "Mis à jour",
"upload": "Importer",
@@ -331,8 +333,8 @@
"any-household": "Tout foyer",
"no-meal-plan-defined-yet": "Aucun menu planifié",
"no-meal-planned-for-today": "Aucun repas prévu pour aujourd'hui",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Nombre de jours passé à charger",
"numberOfDaysPast-label": "Jours par défaut dans le pas",
"numberOfDays-hint": "Nombre de jours lors du chargement de la page",
"numberOfDays-label": "Jours par défaut",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Seules les recettes appartenant à ces catégories seront utilisées dans les menus",
@@ -513,7 +515,7 @@
"milligrams": "milligrammes",
"new-key-name": "Nouveau nom de clé",
"no-white-space-allowed": "Aucun espace blanc autorisé",
"note": "Note",
"note": "Remarque",
"nutrition": "Valeurs nutritionnelles",
"object-key": "Clé d'objet",
"object-value": "Valeur d'objet",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "`BASE_URL` est encore la valeur par défaut sur le serveur API. Cela causera des problèmes avec les liens générés par les notifications sur le serveur pour les e-mails, etc.",
"server-side-base-url-success-text": "L'URL du côté du serveur ne correspond pas à celle par défaut",
"ldap-ready": "Prêt pour LDAP",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP n'est pas prêt",
"ldap-ready-error-text": "Toutes les valeurs LDAP ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous n'utilisez pas l'authentification LDAP.",
"ldap-ready-success-text": "Les variables LDAP obligatoires sont toutes définies.",
"build": "Build",
"recipe-scraper-version": "Version du Scraper de recette",
"oidc-ready": "Prêt pour OIDC",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC n'est pas prêt",
"oidc-ready-error-text": "Toutes les valeurs OIDC ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous nutilisez pas lauthentification OIDC.",
"oidc-ready-success-text": "Les variables OIDC obligatoires sont toutes définies.",
"openai-ready": "Prêt pour OpenAI",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI n'est pas prêt",
"openai-ready-error-text": "Toutes les valeurs OpenAI ne sont pas configurées. Vous pouvez ignorer cet avertissement si vous n'utilisez pas les fonctionnalités OpenAI.",
"openai-ready-success-text": "Les variables OpenAI obligatoires sont toutes définies."
},
@@ -915,8 +917,9 @@
"quantity": "Quantité : {0}",
"shopping-list": "Liste d'épicerie",
"shopping-lists": "Listes d'épicerie",
"add-item": "Ajouter un article",
"food": "Aliments",
"note": "Note",
"note": "Remarque",
"label": "Étiquette",
"save-label": "Sauvegarder le libellé",
"linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Voulez-vous vraiment sélectionner tous les éléments ?",
"are-you-sure-you-want-to-uncheck-all-items": "Voulez-vous vraiment désélectionner tous les éléments ?",
"are-you-sure-you-want-to-delete-checked-items": "Voulez-vous vraiment supprimer tous les éléments sélectionnés ?",
"no-shopping-lists-found": "Aucune liste de courses trouvée"
"no-shopping-lists-found": "Aucune liste de courses trouvée",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Les recettes",
@@ -1347,7 +1351,7 @@
"ingredient-text": "Texte de l'ingrédient",
"average-confident": "Confiant à {0}",
"try-an-example": "Essayez avec un exemple",
"parser": "Parser",
"parser": "Analyseur syntaxique",
"background-tasks": "Tâches en arrière plan",
"background-tasks-description": "Ici vous pouvez voir toutes les tâches en arrière-plan en cours et leur statut",
"no-logs-found": "Pas de journaux trouvés",
@@ -1470,7 +1474,14 @@
"invalid-email": "Le-mail doit être valide",
"invalid-url": "Doit être une URL valide",
"no-whitespace": "Aucun espace n'est autorisé",
"min-length": "",
"min-length": "Doit contenir au moins {min} caractères",
"max-length": "Doit comporter au maximum {max} caractère|Doit comporter au maximum {max} caractères"
},
"announcements": {
"announcements": "Annonces",
"all-announcements": "Toutes les annonces",
"mark-all-as-read": "Marquer tout comme lu",
"show-announcements-from-mealie": "Afficher les annonces de Mealie",
"show-announcements-setting-description": "Si vous voulez autoriser ou non les utilisateurs à voir les annonces de Mealie. Lorsque cette option est activée, les utilisateurs peuvent toujours refuser de les voir dans leurs paramètres utilisateur"
}
}

View File

@@ -19,7 +19,7 @@
"log-lines": "Lignes de log",
"not-demo": "Non",
"portfolio": "Portfolio",
"production": "Production",
"production": "Réalisation",
"support": "Soutenir",
"version": "Version",
"unknown-version": "inconnu",
@@ -98,6 +98,7 @@
"dashboard": "Tableau de bord",
"delete": "Supprimer",
"disabled": "Désactivé",
"done": "Terminé",
"download": "Télécharger",
"duplicate": "Dupliquer",
"edit": "Modifier",
@@ -168,6 +169,7 @@
"token": "Jeton",
"tuesday": "Mardi",
"type": "Type",
"undo": "Undo",
"update": "Mettre à jour",
"updated": "Mis à jour",
"upload": "Importer",
@@ -915,8 +917,9 @@
"quantity": "Quantité: {0}",
"shopping-list": "Liste de courses",
"shopping-lists": "Listes de courses",
"add-item": "Ajouter un article",
"food": "Aliment",
"note": "Note",
"note": "Remarque",
"label": "Étiquette",
"save-label": "Sauvegarder le libellé",
"linked-item-warning": "Cet article est lié à une ou plusieurs recettes. Ajuster les unités ou les aliments donnera des résultats inattendus lors de lajout ou de la suppression de la recette de cette liste.",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Voulez-vous vraiment sélectionner tous les éléments ?",
"are-you-sure-you-want-to-uncheck-all-items": "Voulez-vous vraiment désélectionner tous les éléments ?",
"are-you-sure-you-want-to-delete-checked-items": "Voulez-vous vraiment supprimer tous les éléments sélectionnés ?",
"no-shopping-lists-found": "Aucune liste de courses trouvée"
"no-shopping-lists-found": "Aucune liste de courses trouvée",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Recettes",
@@ -1347,7 +1351,7 @@
"ingredient-text": "Texte de l'ingrédient",
"average-confident": "Confiant à {0}",
"try-an-example": "Essayez avec un exemple",
"parser": "Parser",
"parser": "Analyseur syntaxique",
"background-tasks": "Tâches en arrière-plan",
"background-tasks-description": "Ici vous pouvez voir toutes les tâches en arrière-plan en cours et leur statut",
"no-logs-found": "Pas de journaux trouvés",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Aucun espace n'est autorisé",
"min-length": "Doit contenir au moins {min} caractères",
"max-length": "Doit comporter au maximum {max} caractère|Doit comporter au maximum {max} caractères"
},
"announcements": {
"announcements": "Annonces",
"all-announcements": "Toutes les annonces",
"mark-all-as-read": "Marquer tout comme lu",
"show-announcements-from-mealie": "Afficher les annonces de Mealie",
"show-announcements-setting-description": "Si vous voulez autoriser ou non les utilisateurs à voir les annonces de Mealie. Lorsque cette option est activée, les utilisateurs peuvent toujours refuser de les voir dans leurs paramètres utilisateur"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Panel",
"delete": "Eliminar",
"disabled": "Desactivado",
"done": "Done",
"download": "Descargar",
"duplicate": "Duplicar",
"edit": "Editar",
@@ -168,6 +169,7 @@
"token": "Identificador",
"tuesday": "Martes",
"type": "Tipo",
"undo": "Undo",
"update": "Actualizar",
"updated": "Actualizado",
"upload": "Subir",
@@ -915,6 +917,7 @@
"quantity": "Cantidade: {0}",
"shopping-list": "Lista de Compras",
"shopping-lists": "Listas de Compras",
"add-item": "Add item",
"food": "Comida",
"note": "Nota",
"label": "Rótulo",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Ten a certeza de que pretende selecionar todos os itens?",
"are-you-sure-you-want-to-uncheck-all-items": "Ten a certeza de que pretende desmarcar todos os itens?",
"are-you-sure-you-want-to-delete-checked-items": "Ten a certeza de que pretende eliminar todos os itens selecionados?",
"no-shopping-lists-found": "Nengunha Lista de Compras Encontrada"
"no-shopping-lists-found": "Nengunha Lista de Compras Encontrada",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Todas as Receitas",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "לוח הבקרה",
"delete": "מחיקה",
"disabled": "כבוי",
"done": "Done",
"download": "הורדה",
"duplicate": "שכפול",
"edit": "עריכה",
@@ -168,6 +169,7 @@
"token": "טוקן",
"tuesday": "שלישי",
"type": "סוג",
"undo": "Undo",
"update": "עדכון",
"updated": "עודכן",
"upload": "העלאה",
@@ -424,7 +426,7 @@
"paprika-text": "Mealie יכולה לייבא מתכונים מאפליקציית Paprika. יש לייצא את המתכונים מהאפליקציה, לשנות את סיומת הקובץ ל- zip ולהעלות אותו כאן.",
"mealie-text": "Mealie יכול לייבא מתכונים מגרסאות ישנות של Mealie מתחת ל- v1.0. ייצא את המתכונים מהסביבה הישנה והעלה את קובץ ה- zip למטה. ניתן לייבא רק מתכונים מתהליך הייצוא.",
"plantoeat": {
"title": "Plan to Eat",
"title": "בתכנון לאכול",
"description-long": "Mealie יכולה לייבא מתכונים מהאתר- Plan to Eat."
},
"myrecipebox": {
@@ -644,10 +646,10 @@
"scrape-recipe-suggest-bulk-importer": "נסה את יכולת קריאת רשימה",
"scrape-recipe-have-raw-html-or-json-data": "יש לך מידע גולמי ב-HTML או JSON?",
"scrape-recipe-you-can-import-from-raw-data-directly": "ניתן לייבא ישירות ממידע גולמי",
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-website-being-blocked": "האתר חסום?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "ייבוא שמות מפתח מקוריות כתגיות",
"import-original-categories": "Import original categories",
"import-original-categories": "ייבא קטגוריות מקוריות",
"stay-in-edit-mode": "השאר במצב עריכה",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "ייבא מקובץ",
@@ -693,15 +695,15 @@
"no-unit": "ללא יחידות",
"missing-unit": "יצירת יחידה חסרה: {unit}",
"missing-food": "יצירת אוכל חסר: {food}",
"this-unit-could-not-be-parsed-automatically": "This unit could not be parsed automatically",
"this-food-could-not-be-parsed-automatically": "This food could not be parsed automatically",
"this-unit-could-not-be-parsed-automatically": "יחידת המידה הזאת לא יכלה להיות מחולצת אוטומטית",
"this-food-could-not-be-parsed-automatically": "המאכל הזה לא יכל להיות מחולץ אוטומטית",
"no-food": "אין אוכל",
"review-parsed-ingredients": "Review parsed ingredients",
"confidence-score": "Confidence Score",
"ingredient-parser-description": "Your ingredients have been successfully parsed. Please review the ingredients we're not sure about.",
"ingredient-parser-final-review-description": "Once all ingredients have been reviewed, you'll have one more chance to review all ingredients before applying the changes to your recipe.",
"add-text-as-alias-for-item": "Add \"{text}\" as alias for {item}",
"delete-item": "Delete Item"
"delete-item": "מחק פריט"
},
"reset-servings-count": "איפוס מספר המנות",
"not-linked-ingredients": "מרכיבים נוספים",
@@ -915,6 +917,7 @@
"quantity": "כמות: {0}",
"shopping-list": "רשימת קניות",
"shopping-lists": "רשימות קניות",
"add-item": "Add item",
"food": "אוכל",
"note": "הערה",
"label": "תווית",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "לסמן את כל הפריטים?",
"are-you-sure-you-want-to-uncheck-all-items": "לבטל את סימון כל הפריטים?",
"are-you-sure-you-want-to-delete-checked-items": "למחוק את כל הפריטים המסומנים?",
"no-shopping-lists-found": "לא נמצאה רשימת קניות"
"no-shopping-lists-found": "לא נמצאה רשימת קניות",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "כל המתכונים",
@@ -1094,7 +1098,7 @@
"forgot-password-text": "נא לספק כתובת דוא\"ל. אנו נשלח לך הודעת דוא\"ל לצורך איפוס הסיסמה שלך.",
"changes-reflected-immediately": "שינויים למשתמש זה ישתקפו מיידית.",
"default-activity": "Default Activity",
"default-activity-hint": "Select which page you'd like to navigate to upon logging in from this device"
"default-activity-hint": "בחר את עמוד ברירת המחדל בשביל המכשיר הזה"
},
"language-dialog": {
"translated": "תורגם",
@@ -1153,8 +1157,8 @@
"ounce": "ounce",
"pound": "pound",
"milliliter": "מיליליטר",
"liter": "liter",
"gram": "gram",
"liter": "ליטר",
"gram": "גרם",
"kilogram": "kilogram"
}
},
@@ -1192,7 +1196,7 @@
"edit-recipe-action": "עריכת פעולת-מתכון",
"action-type": "סוג פעולה",
"action-types": {
"link": "Link",
"link": "לינק",
"post": "Post"
}
},
@@ -1466,11 +1470,18 @@
}
},
"validators": {
"required": "This Field is Required",
"required": "חובה למלא שדה זה",
"invalid-email": "Email Must Be Valid",
"invalid-url": "Must Be A Valid URL",
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Kontrolna ploča",
"delete": "Obriši",
"disabled": "Onemogućeno",
"done": "Done",
"download": "Preuzmi",
"duplicate": "Dupliciraj",
"edit": "Uredi",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Utorak",
"type": "Tip",
"undo": "Undo",
"update": "Ažuriraj",
"updated": "Ažurirano",
"upload": "Prenesi",
@@ -915,6 +917,7 @@
"quantity": "Količina: {0}",
"shopping-list": "Popis za Kupovinu",
"shopping-lists": "Popis za Kupovinu",
"add-item": "Add item",
"food": "Namirnica",
"note": "Bilješka",
"label": "Oznaka",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Svi Recepti",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Vezérlőpult",
"delete": "Törlés",
"disabled": "Letiltva",
"done": "Kész",
"download": "Letöltés",
"duplicate": "Duplikálás",
"edit": "Szerkesztés",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Kedd",
"type": "Típus",
"undo": "Visszavonás",
"update": "Frissítés",
"updated": "Frissítve",
"upload": "Feltöltés",
@@ -212,7 +214,7 @@
"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.",
"discard-changes": "Discard Changes",
"discard-changes": "Változtatások elvetése",
"discard-changes-description": "Nem mentett módosításai vannak, biztos, hogy elveti?",
"clipboard-copy-failure": "Nem sikerült a vágólapra másolás.",
"confirm-delete-generic-items": "Biztos benne, hogy törölni szeretné az alábbi tételeket?",
@@ -441,7 +443,7 @@
"error-details": "Csak ld+json vagy microdata-t tartalmazó oldalakat tudunk importálni. Nagyobb weboldalak támogatják ezen adatstruktúrákat. Ha az importálás sikertelen, de van json adat a naplókban, jelentsd a hibát a github issue-kban az URL-el és az naplózott adattal.",
"error-title": "Úgy tűnik nem találtunk semmit",
"from-url": "Recept importálása",
"github-issues": "GitHub Issues",
"github-issues": "GitHub-problémák",
"google-ld-json-info": "ld+json információ keresése",
"must-be-a-valid-url": "Érvényes URL-nek kell lennie",
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Másold be a receptedet. Minden sor egy új elemként lesz kezelve a listában",
@@ -639,7 +641,7 @@
"new-recipe-names-must-be-unique": "Az új recept nevének egyedinek kell lennie",
"scrape-recipe": "Recept kinyerése",
"scrape-recipe-description": "Recept kinyerése URL alapján. Adja meg annak az oldalnak az URLjét, amelyről szeretné a receptet kinyerni, és a Mealie megpróbálja beolvasni a receptet, majd hozzáadja a gyűjteményéhez.",
"scrape-recipe-description-transcription": "You can also provide the url to a video and Mealie will attempt to transcribe it into a recipe.",
"scrape-recipe-description-transcription": "Megadhat egy videóUrlt is, és a Mealie megpróbálja receptté átírni.",
"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",
"scrape-recipe-have-raw-html-or-json-data": "Nyers HTML vagy JSON adatai vannak?",
@@ -915,6 +917,7 @@
"quantity": "Mennyiség: {0}",
"shopping-list": "Bevásárlólista",
"shopping-lists": "Bevásárlólisták",
"add-item": "Tétel hozzáadása",
"food": "Étel",
"note": "Megjegyzés",
"label": "Címke",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Biztos, hogy minden elemet be akar jelölni?",
"are-you-sure-you-want-to-uncheck-all-items": "Biztos, hogy minden elem kijelölését visszavonja?",
"are-you-sure-you-want-to-delete-checked-items": "Biztosan törölni akarja az összes bejelölt elemet?",
"no-shopping-lists-found": "Nem találhatók bevásárlólisták"
"no-shopping-lists-found": "Nem találhatók bevásárlólisták",
"item-checked-off": "{item} leellenőrzve"
},
"sidebar": {
"all-recipes": "Minden recept",
@@ -1343,7 +1347,7 @@
"nlp": "NLP",
"brute": "Brute",
"openai": "OpenAI",
"show-individual-confidence": "",
"show-individual-confidence": "Mutasd az egyes elemek bizalmi szintjét",
"ingredient-text": "Hozzávaló szöveg",
"average-confident": "{0}-os bizonyosság",
"try-an-example": "Próbáljon ki egy példát",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Szóközt nem tartalmazhat",
"min-length": "Legalább {min} karakter legyen",
"max-length": "Legfeljebb {max} karakter lehet|Legfeljebb {max} karakter lehet"
},
"announcements": {
"announcements": "Közlemények",
"all-announcements": "Minden közlemény",
"mark-all-as-read": "Összes megjelölése olvasottként",
"show-announcements-from-mealie": "Mutasd a Mealie értesítéseit",
"show-announcements-setting-description": "Szeretnée engedélyezni, hogy a felhasználók lássák a Mealie bejelentéseit. Ha engedélyezi, a felhasználók a saját beállításaikban továbbra is kikapcsolhatják azokat"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Yfirlitssíða",
"delete": "Eyða",
"disabled": "Afvirkjað",
"done": "Done",
"download": "Sækja",
"duplicate": "Afrita",
"edit": "Breyta",
@@ -168,6 +169,7 @@
"token": "Tóki",
"tuesday": "Þriðjudag",
"type": "Tegund",
"undo": "Undo",
"update": "Uppfæra",
"updated": "Uppfært",
"upload": "Hlaða upp",
@@ -331,8 +333,8 @@
"any-household": "Öll heimili",
"no-meal-plan-defined-yet": "Ekkert matarplan hefur verið skilgreint",
"no-meal-planned-for-today": "Ekkert matarplan skipulagt í dag",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Fjöldi liðina daga við síðuhleðslu",
"numberOfDaysPast-label": "Sjálfgefnir liðnir dagar",
"numberOfDays-hint": "Fjöldi daga við síðuhleðslu",
"numberOfDays-label": "Sjálfgefnir dagar",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Aðeins uppskriftir í þessum flokkum verða notaðir í matarplan",
@@ -638,8 +640,8 @@
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Stofnaðu uppskrift með því að gefa henni nafn, allar uppskriftir þurfa að hafa einstakt nafn.",
"new-recipe-names-must-be-unique": "Nöfn uppskrifta þurfa að vera einstök",
"scrape-recipe": "Vinna uppskrift",
"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-description-transcription": "You can also provide the url to a video and Mealie will attempt to transcribe it into a recipe.",
"scrape-recipe-description": "Sækja uppskrift af vefslóð. Settu inn vefslóð fyrir síðuna þar sem þú vilt sækja uppskrift og Mealie mun reyna að vinna uppskriftina þaðan og bæta henni við safnið þitt.",
"scrape-recipe-description-transcription": "Þú getur einnig sett inn slóð á video og Mealie mun reyna að umrita það yfir í uppskrift.",
"scrape-recipe-have-a-lot-of-recipes": "Ertu með margar uppskriftir sem þú villt setja inn í einu?",
"scrape-recipe-suggest-bulk-importer": "Prófaðu að setja inn margar uppskriftir í einu",
"scrape-recipe-have-raw-html-or-json-data": "Ertu með hrá HTML eða JSON gögn?",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "'BASE_URL' er enn sjálfgefið gildi á API netþjóns. Þetta getur valdið vandræðum með tilkynninga tengla sem netþjónninn býr til fyrir tölvupósta og annað.",
"server-side-base-url-success-text": "Slóð netþjóns samsvarar ekki sjálfgefnu gildi",
"ldap-ready": "LDAP klár",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP er ekki tilbúið",
"ldap-ready-error-text": "Ekki öll LDAP-gildi eru stillt. Þetta má hunsa ef þú notar ekki LDAP-auðkenningu.",
"ldap-ready-success-text": "Öll nauðsynleg LDAP-gildi eru stillt.",
"build": "Build",
"recipe-scraper-version": "Recipe Scraper útgáfa",
"oidc-ready": "OIDC klár",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC er ekki tilbúið",
"oidc-ready-error-text": "Ekki öll OIDC gildi eru stillt. Þetta má hunsa ef þú notar ekki OIDC-auðkenningu.",
"oidc-ready-success-text": "Öll nauðsynleg OIDC-gildi eru stillt.",
"openai-ready": "OpenAI klár",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI er ekki tilbúið",
"openai-ready-error-text": "Ekki öll OpenAI gildi eru stillt. Þetta má hunsa ef þú notar ekki OpenAI.",
"openai-ready-success-text": "Öll nauðsynleg OpenAI-gildi eru stillt."
},
@@ -915,6 +917,7 @@
"quantity": "Fjöldi: {0}",
"shopping-list": "Innkaupalisti",
"shopping-lists": "Innkaupalistar",
"add-item": "Bæta við vöru",
"food": "Matvara",
"note": "Minnispunktur",
"label": "Merkimiði",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Ertu viss um að þú viljir merkja við allar matvörurnar?",
"are-you-sure-you-want-to-uncheck-all-items": "Ertu viss um að þú viljir taka merkingu af öllum matvörunum?",
"are-you-sure-you-want-to-delete-checked-items": "Ertu viss um að þú viljir eyða öllum merktum matvörum?",
"no-shopping-lists-found": "Enginn innkaupalisti fannst"
"no-shopping-lists-found": "Enginn innkaupalisti fannst",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Allar uppskriftir",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Engin bil leyfð",
"min-length": "Verður að vera að lágmarki {min} stafir",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Tilkynningar",
"all-announcements": "Allar tilkynningar",
"mark-all-as-read": "Merkja allar sem lesnar",
"show-announcements-from-mealie": "Sýna tilkynningar frá Mealie",
"show-announcements-setting-description": "Hvort leyfa eigi notendum að sjá tilkynningar fŕa Mealie. Sé þetta virkt þá getur notandinn sjálfur valið að sjá þær ekki í sínum notenda stillingum"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Pannello di controllo",
"delete": "Elimina",
"disabled": "Disabilita",
"done": "Fatto",
"download": "Download",
"duplicate": "Duplicato",
"edit": "Modifica",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Martedì",
"type": "Tipo",
"undo": "Undo",
"update": "Aggiorna",
"updated": "Aggiornato",
"upload": "Carica",
@@ -274,7 +276,7 @@
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Nasconde i campi di alimento, unità e quantità per gli ingredienti e tratta gli ingredienti come campi di testo semplice",
"general-preferences": "Impostazioni Generali",
"group-recipe-preferences": "Impostazioni per le ricette del gruppo",
"report": "Report",
"report": "Segnala",
"report-with-id": "ID Report: {id}",
"group-management": "Gestione Gruppo",
"admin-group-management": "Gestione Gruppo Amministratore",
@@ -346,9 +348,9 @@
"breakfast": "Colazione",
"lunch": "Pranzo",
"dinner": "Cena",
"snack": "Snack",
"snack": "Spuntino",
"drink": "Bevanda",
"dessert": "Dessert",
"dessert": "Dolce",
"type-any": "Qualsiasi",
"day-any": "Qualsiasi",
"editor": "Modifica",
@@ -424,7 +426,7 @@
"paprika-text": "Mealie può importare ricette dall'applicazione Paprika. Esporta le tue ricette da paprika, rinomina l'estensione di esportazione in .zip e caricala qui sotto.",
"mealie-text": "Mealie può importare ricette dall'applicazione Mealie da un versione pre v1.0. Esporta le tue ricette dalla tua vecchia istanza e carica il file zip qui sotto. Nota che solo le ricette possono essere importate dall'esportazione.",
"plantoeat": {
"title": "Plan to Eat",
"title": "Pianifico di mangiare",
"description-long": "Mealie può importare le ricette da Plan to Eat."
},
"myrecipebox": {
@@ -441,7 +443,7 @@
"error-details": "Solo i siti web contenenti ld+json o microdata possono essere importati da Mealie. Se il tuo sito non può essere importato, ma ci sono dati json nel log, per favore crea una issue su github con l'URL e i dati.",
"error-title": "Sembra che non riusciamo a trovare nulla",
"from-url": "Importa ricetta",
"github-issues": "GitHub Issues",
"github-issues": "Github Issues",
"google-ld-json-info": "Google ld+json Info",
"must-be-a-valid-url": "Deve essere un URL valido",
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Inserisci i dati della tua ricetta. Ogni riga sarà trattata come uno step della ricetta.",
@@ -544,7 +546,7 @@
"share-recipe-message": "Volevo condividere la mia {0} ricetta con te.",
"show-nutrition-values": "Mostra Valori Nutrizionali",
"sodium-content": "Sodio",
"step-index": "Step: {step}",
"step-index": "Passo {step}",
"sugar-content": "Zuccheri",
"title": "Titolo",
"total-time": "Tempo Totale",
@@ -634,7 +636,7 @@
"please-wait-image-procesing": "Attendere, l'immagine è in fase di elaborazione. Potrebbe volerci un po' di tempo.",
"please-wait-images-processing": "Attendere, le immagini sono in fase di elaborazione. Potrebbe volerci un po' di tempo.",
"bulk-url-import": "Importazione multipla URL",
"debug-scraper": "Debug Scraper",
"debug-scraper": "Scraper Debug",
"create-a-recipe-by-providing-the-name-all-recipes-must-have-unique-names": "Crea una ricetta fornendo il nome. Tutte le ricette devono avere nomi univoci.",
"new-recipe-names-must-be-unique": "I nuovi nomi delle ricette devono essere univoci",
"scrape-recipe": "Recupera Ricetta",
@@ -867,7 +869,7 @@
"bug-report-information": "Usa queste informazioni per segnalare un bug. Fornire i dettagli della tua istanza agli sviluppatori è il modo migliore per risolvere rapidamente i tuoi problemi.",
"tracker": "Invia",
"configuration": "Configurazione",
"docker-volume": "Docker Volume",
"docker-volume": "Volume di Docker",
"docker-volume-help": "Mealie richiede che il frontend e il backend condividano lo stesso volume docker o archiviazione. Ciò assicura che il frontend possa accedere correttamente alle immagini e alle risorse memorizzate sul disco.",
"volumes-are-misconfigured": "I volumi sono configurati male.",
"volumes-are-configured-correctly": "I volumi sono stati configurati correttamente.",
@@ -915,6 +917,7 @@
"quantity": "Quantità: {0}",
"shopping-list": "Lista della Spesa",
"shopping-lists": "Liste della Spesa",
"add-item": "Add item",
"food": "Alimenti",
"note": "Nota",
"label": "Etichetta",
@@ -939,14 +942,15 @@
"are-you-sure-you-want-to-check-all-items": "Sei sicuro di voler tutti gli elementi?",
"are-you-sure-you-want-to-uncheck-all-items": "Sei sicuro di voler deselezionare tutti gli elementi?",
"are-you-sure-you-want-to-delete-checked-items": "Sei sicuro di voler rimuovere tutti gli articoli selezionati?",
"no-shopping-lists-found": "Nessuna lista della spesa trovata"
"no-shopping-lists-found": "Nessuna lista della spesa trovata",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Ricette",
"backups": "Backup",
"categories": "Categorie",
"cookbooks": "Ricettari",
"dashboard": "Dashboard",
"dashboard": "Pannello iniziale",
"home-page": "Pagina iniziale",
"manage-users": "Gestire Utenti",
"migrations": "Migrazioni",
@@ -958,7 +962,7 @@
"language": "Lingua",
"maintenance": "Manutenzione",
"background-tasks": "Attività in Background",
"parser": "Parser",
"parser": "Analizzatore",
"developer": "Sviluppatore",
"cookbook": "Ricettario",
"create-cookbook": "Crea un nuovo ricettario"
@@ -1016,12 +1020,12 @@
"full-name": "Nome",
"generate-password-reset-link": "Genera Link Di Reset Password",
"invite-only": "Solo su invito",
"link-id": "Link ID",
"link-id": "Link-ID",
"link-name": "Link Nome",
"login": "Login",
"login": "Accedi",
"login-oidc": "Accedi con",
"or": "oppure",
"logout": "Logout",
"logout": "Esci",
"manage-users": "Gestisci Utenti",
"manage-users-description": "Crea e gestisci gli utenti.",
"new-password": "Nuova Password",
@@ -1433,7 +1437,7 @@
"require-all-tools": "Richiedi Tutti Gli Strumenti",
"cookbook-name": "Nome Ricettario",
"cookbook-with-name": "Ricettario {0}",
"household-cookbook-name": "{0} Cookbook {1}",
"household-cookbook-name": "{0} Ricettario {1}",
"create-a-cookbook": "Crea un libro di cucina",
"cookbook": "Libro di cucina"
},
@@ -1472,5 +1476,12 @@
"no-whitespace": "Gli Spazi Non Sono Ammessi",
"min-length": "Deve Essere Almeno {min} Caratteri",
"max-length": "Deve Essere Al Più {max} Carattere|Deve Essere Al Più {max} Caratteri"
},
"announcements": {
"announcements": "Annunci",
"all-announcements": "Tutti gli annunci",
"mark-all-as-read": "Segna tutti come letti",
"show-announcements-from-mealie": "Mostra annunci da Mealie",
"show-announcements-setting-description": "Consentire o meno agli utenti di vedere gli annunci da Mealie. Quando abilitato gli utenti potranno comunque scegliere di non vederli dalle impostazioni utente."
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "ダッシュボード",
"delete": "削除",
"disabled": "無効",
"done": "Done",
"download": "ダウンロード",
"duplicate": "複製",
"edit": "編集",
@@ -168,6 +169,7 @@
"token": "トークン",
"tuesday": "火曜日",
"type": "タイプ",
"undo": "Undo",
"update": "更新",
"updated": "更新日時",
"upload": "アップロード",
@@ -915,6 +917,7 @@
"quantity": "数量: {0}",
"shopping-list": "買い物リスト",
"shopping-lists": "買い物リスト",
"add-item": "Add item",
"food": "食料",
"note": "メモ",
"label": "ラベル",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "すべての項目をチェックしてもよろしいですか?",
"are-you-sure-you-want-to-uncheck-all-items": "すべてのアイテムのチェックを外してもよろしいですか?",
"are-you-sure-you-want-to-delete-checked-items": "チェックされた項目をすべて削除してもよろしいですか?",
"no-shopping-lists-found": "ショッピングリストが見つかりません"
"no-shopping-lists-found": "ショッピングリストが見つかりません",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "すべてのレシピ",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "대시보드",
"delete": "삭제",
"disabled": "비활성화됨",
"done": "Done",
"download": "다운로드",
"duplicate": "복제",
"edit": "편집",
@@ -168,6 +169,7 @@
"token": "토큰",
"tuesday": "화요일",
"type": "유형",
"undo": "Undo",
"update": "업데이트",
"updated": "업데이트됨",
"upload": "업로드",
@@ -182,7 +184,7 @@
"start": "시작",
"toggle-view": "뷰 전환",
"date": "날짜",
"id": "",
"id": "Id",
"owner": "작성자",
"change-owner": "소유자 변경",
"date-added": "추가된 날짜",
@@ -915,6 +917,7 @@
"quantity": "양: {0}",
"shopping-list": "장보기 목록",
"shopping-lists": "장보기 목록",
"add-item": "Add item",
"food": "식품",
"note": "메모",
"label": "라벨",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "모든 항목을 선택하시겠습니까?",
"are-you-sure-you-want-to-uncheck-all-items": "모든 항목을 선택 해제하시겠습니까?",
"are-you-sure-you-want-to-delete-checked-items": "선택한 항목을 모두 삭제하시겠습니까?",
"no-shopping-lists-found": "장보기 목록이 없습니다."
"no-shopping-lists-found": "장보기 목록이 없습니다.",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "모든 레시피",
@@ -1472,5 +1476,12 @@
"no-whitespace": "공백 허용 안 됨",
"min-length": "최소 {min}자 이상이어야 합니다",
"max-length": "최대 {max}자|최대 {max}자\n"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Prietaisų skydelis",
"delete": "Ištrinti",
"disabled": "Išjungta",
"done": "Done",
"download": "Atsisiųsti",
"duplicate": "Sukurti kopiją",
"edit": "Redaguoti",
@@ -168,6 +169,7 @@
"token": "Prieeigos raktas",
"tuesday": "Antradienis",
"type": "Tipas",
"undo": "Undo",
"update": "Atnaujinti",
"updated": "Atnaujinta",
"upload": "Įkelti",
@@ -915,6 +917,7 @@
"quantity": "Kiekis: {0}",
"shopping-list": "Pirkinių sąrašas",
"shopping-lists": "Pirkinių sąrašai",
"add-item": "Add item",
"food": "Maistas",
"note": "Pastaba",
"label": "Žyma",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Visi receptai",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Informācijas panelis",
"delete": "Dzēst",
"disabled": "Atspējots",
"done": "Done",
"download": "Lejupielādēt",
"duplicate": "Dublēt",
"edit": "Rediģēt",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Otrdiena",
"type": "Tips",
"undo": "Undo",
"update": "Atjaunināt",
"updated": "Atjaunināts",
"upload": "Augšupielādēt",
@@ -915,6 +917,7 @@
"quantity": "Daudzums: {0}",
"shopping-list": "Iepirkumu saraksts",
"shopping-lists": "Iepirkumu saraksti",
"add-item": "Add item",
"food": "Pārtika",
"note": "piezīme",
"label": "etiķete",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Vai esat pārliecināts, ka vēlaties pārbaudīt visus vienumus?",
"are-you-sure-you-want-to-uncheck-all-items": "Vai esat pārliecināts, ka vēlaties noņemt atzīmi no visiem vienumiem?",
"are-you-sure-you-want-to-delete-checked-items": "Vai tiešām vēlaties dzēst visus pārbaudītos vienumus?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Visas receptes",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -51,7 +51,7 @@
"category": "Categorie"
},
"events": {
"apprise-url": "Apprise URL",
"apprise-url": "Kennisgevings-url",
"database": "Database",
"delete-event": "Gebeurtenis verwijderen",
"event-delete-confirmation": "Weet je zeker dat je deze gebeurtenis wilt verwijderen?",
@@ -98,6 +98,7 @@
"dashboard": "Dashboard",
"delete": "Verwijderen",
"disabled": "Uitgeschakeld",
"done": "Gereed",
"download": "Downloaden",
"duplicate": "Dupliceren",
"edit": "Bewerken",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "dinsdag",
"type": "Soort",
"undo": "Ongedaan maken",
"update": "Bijwerken",
"updated": "Bijgewerkt",
"upload": "Uploaden",
@@ -331,8 +333,8 @@
"any-household": "Elk huishouden",
"no-meal-plan-defined-yet": "Nog geen maaltijdplan opgesteld",
"no-meal-planned-for-today": "Geen maaltijd gepland voor vandaag",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Aantal dagen in het verleden bij laden pagina",
"numberOfDaysPast-label": "Standaard dagen in het verleden",
"numberOfDays-hint": "Aantal dagen bij laden van de pagina",
"numberOfDays-label": "Standaard aantal dagen",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Alleen recepten met deze categorieën zullen worden gebruikt in maaltijdplannen",
@@ -441,7 +443,7 @@
"error-details": "Alleen websites met ld+json of microdata kunnen worden geïmporteerd door Mealie. De meeste grote receptenwebsites ondersteunen deze gegevensstructuur. Als je site niet kan worden geïmporteerd, maar er zijn json-gegevens in de log, maak dan een github issue aan met de URL en gegevens.",
"error-title": "Het lijkt erop dat we niets konden vinden",
"from-url": "Recept importeren",
"github-issues": "GitHub Issues",
"github-issues": "GitHubproblemen",
"google-ld-json-info": "Google ld+json Info",
"must-be-a-valid-url": "Moet een geldige URL zijn",
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Plak je receptgegevens. Elke regel wordt behandeld als een item in een lijst",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "`BASE_URL` is nog steeds de standaard waarde op de API Server. Dit geeft problemen met notificatielinks in e-mails etc.",
"server-side-base-url-success-text": "Server-side URL komt niet overeen met de standaard",
"ldap-ready": "LDAP klaar",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP niet gereed",
"ldap-ready-error-text": "Niet alle LDAP-waarden zijn geconfigureerd. Dit kan worden genegeerd als je geen LDAP-authenticatie gebruikt.",
"ldap-ready-success-text": "Vereiste LDAP variabelen zijn helemaal ingesteld.",
"build": "Build",
"recipe-scraper-version": "Versie van de receptenscraper",
"oidc-ready": "OIDC klaar",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC niet gereed",
"oidc-ready-error-text": "Niet alle OIDC-waarden zijn geconfigureerd. Dit kan worden genegeerd als je geen OIDC-authenticatie gebruikt.",
"oidc-ready-success-text": "Vereiste OIDC-variabelen zijn allemaal ingesteld.",
"openai-ready": "OpenAI staat klaar",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI niet gereed",
"openai-ready-error-text": "Niet alle tekstvakken voor OpenAI zijn ingevuld. Als je geen OpenAI gebruikt kun je dit leeg laten.",
"openai-ready-success-text": "Verplichte tekstvakken voor OpenAI zijn ingevuld."
},
@@ -915,6 +917,7 @@
"quantity": "Hoeveelheid: {0}",
"shopping-list": "Boodschappenlijst",
"shopping-lists": "Boodschappenlijsten",
"add-item": "Item toevoegen",
"food": "Levensmiddelen",
"note": "Notitie",
"label": "Label",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Weet je zeker dat je alle items wilt selecteren?",
"are-you-sure-you-want-to-uncheck-all-items": "Weet je zeker dat je alle items wilt deselecteren?",
"are-you-sure-you-want-to-delete-checked-items": "Weet je zeker dat je de geselecteerde items wilt verwijderen?",
"no-shopping-lists-found": "Geen boodschappenlijsten gevonden"
"no-shopping-lists-found": "Geen boodschappenlijsten gevonden",
"item-checked-off": "Uitgevinkt {item}"
},
"sidebar": {
"all-recipes": "Alle Recepten",
@@ -1279,7 +1283,7 @@
"split-by-block": "Splits per tekstblok",
"flatten": "Plat maken ongeacht originele opmaak",
"help": {
"help": "Help",
"help": "Hulp",
"mouse-modes": "Muismodus",
"selection-mode": "Selectiemodus (standaard)",
"selection-mode-desc": "De selectiemodus is de hoofdmodus die gebruikt kan worden om gegevens in te voeren:",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Geen spaties toegestaan",
"min-length": "Moet minimaal {min} tekens bevatten",
"max-length": "Moet maximaal {max} tekens bevatten|Moet maximaal {max} tekens bevatten"
},
"announcements": {
"announcements": "Aankondigingen",
"all-announcements": "Alle aankondigingen",
"mark-all-as-read": "Alles markeren als gelezen",
"show-announcements-from-mealie": "Aankondigingen van Mealie weergeven",
"show-announcements-setting-description": "Of je gebruikers wel of niet meldingen van Mealie wilt laten zien. Wanneer ingeschakeld kunnen gebruikers nog steeds afzien van het bekijken van hen in hun gebruikersinstellingen"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Kontrollpanel",
"delete": "Slett",
"disabled": "Deaktivert",
"done": "Ferdig",
"download": "Last ned",
"duplicate": "Dupliser",
"edit": "Rediger",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Tirsdag",
"type": "Type",
"undo": "Angre",
"update": "Oppdater",
"updated": "Oppdatert",
"upload": "Last opp",
@@ -332,7 +334,7 @@
"no-meal-plan-defined-yet": "Ingen måltidsplan er definert ennå",
"no-meal-planned-for-today": "Ingen måltid planlagt i dag",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-label": "Standard antall dager tilbake",
"numberOfDays-hint": "Antall dager på sideinnlasting",
"numberOfDays-label": "Standard antall dager",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Kun oppskrifter med disse kategoriene vil bli brukt i måltidsplaner",
@@ -390,7 +392,7 @@
"nextcloud": {
"description": "Overfør data fra en Nextcloud Cookbook-instans",
"description-long": "Oppskrifter fra Nextcloud kan importeres fra en zip-fil som inneholder dataene lagret i Nextcloud. Se eksempelet på mappestrukture nedenfor for å sikre at oppskriftene kan importeres.",
"title": "Nextcloud Cookbook"
"title": "Nextcloud kokebok"
},
"copymethat": {
"description-long": "Mealie kan importere oppskrifter fra Copy Me That. Eksporter oppskrifter i HTML-format, last deretter opp .zip-filen under.",
@@ -915,6 +917,7 @@
"quantity": "Antall: {0}",
"shopping-list": "Handleliste",
"shopping-lists": "Handlelister",
"add-item": "Legg til produkt",
"food": "Matvare",
"note": "Notat",
"label": "Etikett",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Er du sikker på at du vil velge alle elementer?",
"are-you-sure-you-want-to-uncheck-all-items": "Er du sikker på at du vil fjerne valg av alle elementer?",
"are-you-sure-you-want-to-delete-checked-items": "Er du sikker på at du vil slette alle valgte elementer?",
"no-shopping-lists-found": "Ingen handlelister funnet"
"no-shopping-lists-found": "Ingen handlelister funnet",
"item-checked-off": "Avkrysset av {item}"
},
"sidebar": {
"all-recipes": "Alle oppskrifter",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Ingen mellomrom tillatt",
"min-length": "Må minst ha {min} tegn",
"max-length": "Må være minst minst {max} tegn må bestå av maks {max} tegn"
},
"announcements": {
"announcements": "Kunngjøringer",
"all-announcements": "Alle kunngjøringer",
"mark-all-as-read": "Marker alle som lest",
"show-announcements-from-mealie": "Vis kunngjøringer fra Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -51,7 +51,7 @@
"category": "Kategoria"
},
"events": {
"apprise-url": "Apprise URL",
"apprise-url": "URL Apprise",
"database": "Baza danych",
"delete-event": "Usuń wydarzenie",
"event-delete-confirmation": "Czy na pewno chcesz usunąć to zdarzenie?",
@@ -98,6 +98,7 @@
"dashboard": "Panel główny",
"delete": "Usuń",
"disabled": "Wyłączone",
"done": "Gotowe",
"download": "Pobierz",
"duplicate": "Duplikuj",
"edit": "Edytuj",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Wtorek",
"type": "Typ",
"undo": "Cofnij",
"update": "Zaktualizuj",
"updated": "Zaktualizowano",
"upload": "Prześlij",
@@ -915,6 +917,7 @@
"quantity": "Ilość: {0}",
"shopping-list": "Lista zakupów",
"shopping-lists": "Listy zakupów",
"add-item": "Dodaj element",
"food": "Jedzenie",
"note": "Notatka",
"label": "Etykieta",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Czy na pewno chcesz zaznaczyć wszystkie elementy?",
"are-you-sure-you-want-to-uncheck-all-items": "Czy na pewno chcesz odznaczyć wszystkie elementy?",
"are-you-sure-you-want-to-delete-checked-items": "Czy jesteś pewien, że chcesz usunąć wszystkie zaznaczone elementy?",
"no-shopping-lists-found": "Nie znaleziono list zakupów"
"no-shopping-lists-found": "Nie znaleziono list zakupów",
"item-checked-off": "Zaznaczono {item}"
},
"sidebar": {
"all-recipes": "Wszystkie",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Niedozwolone są puste spacje",
"min-length": "Musi zawierać co najmniej {min} znaków",
"max-length": "Może zawierać co najwyżej {max} znak|Może zawierać co najwyżej {max} znaki|Może zawierać co najwyżej {max} znaków"
},
"announcements": {
"announcements": "Ogłoszenia",
"all-announcements": "Wszystkie ogłoszenia",
"mark-all-as-read": "Oznacz wszystkie jako przeczytane",
"show-announcements-from-mealie": "Pokazuj ogłoszenia z Mealie",
"show-announcements-setting-description": "Czy chcesz by użytkownicy widzieli ogłoszenia z Mealie? Użytkownicy będą w dalszym ciągu mogli wyłączyć ogłoszenia w swoich ustawieniach użytkownika"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Painel de controle",
"delete": "Excluir",
"disabled": "Desabilitado",
"done": "Done",
"download": "Baixar",
"duplicate": "Duplicar",
"edit": "Editar",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Terça-feira",
"type": "Tipo",
"undo": "Undo",
"update": "Atualizar",
"updated": "Atualizado",
"upload": "Enviar",
@@ -915,6 +917,7 @@
"quantity": "Quantidade: {0}",
"shopping-list": "Lista de compras",
"shopping-lists": "Listas de compras",
"add-item": "Add item",
"food": "Comida",
"note": "Nota",
"label": "Marcador",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Tem certeza que deseja marcar todos os itens?",
"are-you-sure-you-want-to-uncheck-all-items": "Tem certeza que deseja desmarcar todos os itens?",
"are-you-sure-you-want-to-delete-checked-items": "Tem certeza que deseja apagar todos os itens marcados?",
"no-shopping-lists-found": "Nenhuma lista de compras encontrada"
"no-shopping-lists-found": "Nenhuma lista de compras encontrada",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Todas as Receitas",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Nenhum espaço em branco é permitido",
"min-length": "Precisa ter pelo menos {min} caracteres",
"max-length": "Precisa ter no máximo {max} caractere|Precisa ter no máximo {max} caracteres"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Painel de controlo",
"delete": "Eliminar",
"disabled": "Desativado",
"done": "Done",
"download": "Transferir",
"duplicate": "Duplicado",
"edit": "Editar",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Terça-feira",
"type": "Tipo",
"undo": "Undo",
"update": "Atualizar",
"updated": "Atualizado",
"upload": "Enviar",
@@ -332,7 +334,7 @@
"no-meal-plan-defined-yet": "Nenhum plano de refeições definido",
"no-meal-planned-for-today": "Nenhum plano de refeições definido para hoje",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-label": "",
"numberOfDays-hint": "Número de dias no carregamento da página",
"numberOfDays-label": "Dias predefinidos",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Só serão usadas receitas com estas categorias nos Planos de Refeições",
@@ -915,6 +917,7 @@
"quantity": "Quantidade: {0}",
"shopping-list": "Lista de Compras",
"shopping-lists": "Listas de Compras",
"add-item": "Add item",
"food": "Alimentos",
"note": "Nota",
"label": "Rótulo",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Tem a certeza de que pretende selecionar todos os itens?",
"are-you-sure-you-want-to-uncheck-all-items": "Tem a certeza de que pretende desmarcar todos os itens?",
"are-you-sure-you-want-to-delete-checked-items": "Tem a certeza de que pretende eliminar todos os itens selecionados?",
"no-shopping-lists-found": "Nenhuma lista de compras encontrada"
"no-shopping-lists-found": "Nenhuma lista de compras encontrada",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Todas as Receitas",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Não são permitidos espaços em branco",
"min-length": "Deve ter pelo menos {min} caracteres",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Panou de control",
"delete": "Șterge",
"disabled": "Inactiv",
"done": "Done",
"download": "Descarcă",
"duplicate": "Duplicat",
"edit": "Editează",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Marţi",
"type": "Tip",
"undo": "Undo",
"update": "Actualizează",
"updated": "Actualizat",
"upload": "Încarcă",
@@ -915,6 +917,7 @@
"quantity": "Cantitate: {0}",
"shopping-list": "Listă de cumpărături",
"shopping-lists": "Liste de cumpărături",
"add-item": "Add item",
"food": "Aliment",
"note": "Note",
"label": "Etichetă",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Sunteți sigur că doriți să bifați toate elementele?",
"are-you-sure-you-want-to-uncheck-all-items": "Sunteți sigur că doriți să debifați toate elementele?",
"are-you-sure-you-want-to-delete-checked-items": "Sunteți sigur că doriți să ștergeți toate elementele selectate?",
"no-shopping-lists-found": "Nu s-au găsit liste de cumpărături"
"no-shopping-lists-found": "Nu s-au găsit liste de cumpărături",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Toate reţetele",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Niciun Spațiu Alb Permis",
"min-length": "Trebuie Să Aibă Cel Puțin {min} Caractere",
"max-length": "Trebuie să aibă cel mult {max} caracter | Trebuie să aibă cel mult {max} caractere"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Панель инструментов",
"delete": "Удалить",
"disabled": "Отключено",
"done": "Done",
"download": "Загрузить",
"duplicate": "Создать копию",
"edit": "Изменить",
@@ -168,6 +169,7 @@
"token": "Токен",
"tuesday": "Вторник",
"type": "Тип",
"undo": "Undo",
"update": "Обновить",
"updated": "Обновлено",
"upload": "Загрузить",
@@ -915,6 +917,7 @@
"quantity": "Количество: {0}",
"shopping-list": "Список покупок",
"shopping-lists": "Списки покупок",
"add-item": "Add item",
"food": "Продукты",
"note": "Заметка",
"label": "Метка",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Вы уверены, что хотите выбрать все элементы?",
"are-you-sure-you-want-to-uncheck-all-items": "Вы уверены, что хотите снять отметку со всех элементов?",
"are-you-sure-you-want-to-delete-checked-items": "Вы уверены, что хотите удалить все отмеченные элементы?",
"no-shopping-lists-found": "Списки покупок не найдены"
"no-shopping-lists-found": "Списки покупок не найдены",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Все рецепты",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Без пробелов",
"min-length": "Должно быть минимум {min} символов",
"max-length": "Должно быть как минимум {max} знак|Должно быть больше {max} знаков"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Hlavný panel",
"delete": "Odstrániť",
"disabled": "Deaktivované",
"done": "Hotovo",
"download": "Stiahnuť",
"duplicate": "Duplikovať",
"edit": "Upraviť",
@@ -109,7 +110,7 @@
"field-required": "Povinný údaj",
"file-folder-not-found": "Súbor/priečinok nenájdený",
"file-uploaded": "Súbor nahratý",
"filter": "Filter",
"filter": "Filtrovať",
"friday": "Piatok",
"general": "Všeobecné",
"get": "Získať",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Utorok",
"type": "Typ",
"undo": "Undo",
"update": "Aktualizovať",
"updated": "Aktualizované",
"upload": "Nahrať",
@@ -915,6 +917,7 @@
"quantity": "Množstvo: {0}",
"shopping-list": "Nákupný zoznam",
"shopping-lists": "Nákupné zoznamy",
"add-item": "Add item",
"food": "Jedlo",
"note": "Poznámka",
"label": "Štítok",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Naozaj chcete označiť všetky položky?",
"are-you-sure-you-want-to-uncheck-all-items": "Naozaj chcete zrušiť označenie všetkých položiek?",
"are-you-sure-you-want-to-delete-checked-items": "Naozaj chcete odstrániť všetky označené položky?",
"no-shopping-lists-found": "Nenašli sa žiadne nákupné zoznamy"
"no-shopping-lists-found": "Nenašli sa žiadne nákupné zoznamy",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Všetky recepty",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Prázdne znaky nepovolené",
"min-length": "Musí mať aspoň {min} znakov",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Nadzorna plošča",
"delete": "Izbriši",
"disabled": "Onemogočeno",
"done": "Končano",
"download": "Prenesi",
"duplicate": "Podvoji",
"edit": "Uredi",
@@ -168,6 +169,7 @@
"token": "Žeton",
"tuesday": "Torek",
"type": "Tip",
"undo": "Razveljavi",
"update": "Posodobi",
"updated": "Posodobljen",
"upload": "Naloži",
@@ -385,12 +387,12 @@
"chowdown": {
"description": "Migriraj podatke iz Chowdown-a",
"description-long": "Mealie podpira chowdown format shrambe podatkov. Prenesi shrambo podatkov kot .zip datoteko in jo naloži spodaj.",
"title": "Chowdown"
"title": "Prehranjevanje"
},
"nextcloud": {
"description": "Migriraj podatke iz Nextcloud Cookbook",
"description-long": "Nextcloud recepte lahko uvoziš z zip datoteko, ki vsebuje podatke shranjene na Nextcloudu. Poglej primer strukture mape spodaj, da zagotoviš primerno strukturo za uvoz receptov.",
"title": "Nextcloud Cookbook"
"title": "Kuharska knjiga Nextcloud"
},
"copymethat": {
"description-long": "Mealie lahko uvozi recepte iz aplikacije Copy Me That. Izvozi recepte v HTML format in spodaj naloži .zip datoteko.",
@@ -398,11 +400,11 @@
},
"paprika": {
"description-long": "Mealie lahko uvozi recepte iz aplikacije Paprika. Izvozi recepte iz aplikacije Paprika, preimenuj končnico datoteke v .zip in jo naloži spodaj.",
"title": "Paprika Recipe Manager"
"title": "Urejevalnik receptov Paprika"
},
"mealie-pre-v1": {
"description-long": "Mealie lahko uvozi recepte izpred Mealie verzije v1.0. Izvozi recepte iz stare instance in spodaj uvozi zip datoteko. Omogočen je uvoz samo za recepte.",
"title": "Mealie Pre v1.0"
"title": "Mealie pred v1.0"
},
"tandoor": {
"description-long": "Mealie lahko uvozi recepte iz aplikacije Tandoor. Izvozi podatke v \"Default\" formatu, nato spodaj naloži .zip datoteko.",
@@ -424,15 +426,15 @@
"paprika-text": "Mealie lahko uvozi recepte iz aplikacije Paprika. Izvozi recepte iz aplikacije Paprika, preimenuj končnico datoteke v .zip in jo naloži spodaj.",
"mealie-text": "Mealie lahko uvozi recepte izpred Mealie verzije v1.0. Izvozi recepte iz stare instance in spodaj uvozi zip datoteko. Omogočen je uvoz samo za recepte.",
"plantoeat": {
"title": "Plan to Eat",
"title": "Načrt za pojesti",
"description-long": "Mealie lahko uvozi recepte iz aplikacije Plan to Eat."
},
"myrecipebox": {
"title": "My Recipe Box",
"title": "Moj zaboj z recepti",
"description-long": "Mealie lahko uvozi recepte iz aplikacije My Recipe Box. Izvozi recepte v CSV formatu, nato pa .csv datoteko naloži spodaj."
},
"recipekeeper": {
"title": "Recipe Keeper",
"title": "Hranilec receptov",
"description-long": "Mealie lahko uvozi recepte iz Recipe Keeper. Izvozi svoje recepte v .zip formatu, nato spodaj naloži .zip datoteko."
}
},
@@ -556,7 +558,7 @@
"join-the-conversation": "Pridruži se pogovoru",
"add-recipe-to-mealplan": "Dodaj recept v jedilnik",
"entry-type": "Tip vnosa",
"date-format-hint": "MM/DD/YYYY format",
"date-format-hint": "MM/DD/YYYY oblika",
"date-format-hint-yyyy-mm-dd": "YYYY-MM-DD oblika",
"add-to-list": "Dodaj na seznam",
"add-to-plan": "Dodaj v načrt",
@@ -670,7 +672,7 @@
"recipe-debugger-description": "Prilepi povezavo do recepta, ki ga želiš postrgati. Strgalnik receptov bo postrgal spletno stran in prikazal rezultate. Če ne vidiš nobenih podatkov, potem spletna stran, ki jo želiš strgati ni podprta v Mealie oz. v strgalniku, ki ga uporablja.",
"use-openai": "Uporabi OpenAI",
"recipe-debugger-use-openai-description": "Za razčlenitev rezultatov uporabite OpenAI, namesto da se zanašate na knjižnico strgala. Ko ustvarjate recept prek URL-ja, se to izvede samodejno, če knjižnica strgala odpove, vendar ga lahko tukaj preizkusite ročno.",
"debug": "Debug",
"debug": "Razhroščevanje",
"tree-view": "Drevesni prikaz",
"recipe-servings": "Rocept obrokov",
"recipe-yield": "Število porcij",
@@ -857,17 +859,17 @@
"webhooks": {
"test-webhooks": "Test Webhook-ov",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "Spodnji URL naslovi bodo prejeli webhook-e s podatki receptov za načrt obrokov na načrtovani dan. Trenutno se bodo webhooki-i izvedli ob",
"webhook-url": "Webhook URL",
"webhooks-caps": "WEBHOOKS",
"webhook-url": "URL spletnega kavlja",
"webhooks-caps": "SPLETNI KAVLJI",
"webhooks": "Webbhook-i",
"webhook-name": "Ime Webhooka",
"description": "Spodaj definirani webhooki bodo izvedeni na dneve, ki imajo definirane obroke. Ob določenem času bodo na webhook poslani podatki iz recepta, ki je planiran na ta dan. Upoštevaj, da izvedba webhookov ni točna. Webhooki se izvajajo v 5 minutnih intervalih, kar pomeni, da bo webhook izveden v okviru +/- 5 minut od planiranega časa."
},
"bug-report": "Prijava napake",
"bug-report-information": "Uporabi te podatke ob prijavi napake. Sporočanje podrobnosti o tvoji instanci pomaga razvijalcem najhitreje odpraviti tvojo težavo.",
"tracker": "Tracker",
"tracker": "Sledilnik",
"configuration": "Nastavitve",
"docker-volume": "Docker Volume",
"docker-volume": "Docker mapa",
"docker-volume-help": "Mealie zahteva, da vsebnik sprednjega dela in zadnji del delita isto mapo ali shrambo priklopne postaje. To zagotavlja, da lahko sprednji vsebnik pravilno dostopa do slik in sredstev, shranjenih na disku.",
"volumes-are-misconfigured": "Mape so napačno nastavljene.",
"volumes-are-configured-correctly": "Mape so nastavljene pravilno.",
@@ -876,8 +878,8 @@
"email-configuration-status": "Email status",
"email-configured": "Email nastavljen",
"email-test-results": "Email test rezultati",
"ready": "Ready",
"not-ready": "Not Ready - Check Environmental Variables",
"ready": "Pripravljeno",
"not-ready": "Ni pripravljeno - Preverite okoljske spremenljivke",
"succeeded": "Uspelo je",
"failed": "Ni uspelo",
"general-about": "Splošne informacije",
@@ -890,13 +892,13 @@
"server-side-base-url": "Strežniški URL",
"server-side-base-url-error-text": "`BASE_URL` se še vedno ujema s privzeto vrednostjo API strežnika. To bo povzročalo težave s povezavami do obvestil generiranih za emaile ipd.",
"server-side-base-url-success-text": "Strežniški URL se ne ujema s privzeto vrednostjo",
"ldap-ready": "LDAP Ready",
"ldap-ready": "Pripravljeno za LDAP",
"ldap-not-ready": "LDAP ni pripravljen",
"ldap-ready-error-text": "Nekatere LDAP vrednosti niso nastavljene. To lahko ignoriraš, če ne uporabljaš LDAP avtentikacije.",
"ldap-ready-success-text": "Vse potrebne LDAP spremenljivke so nastavljene.",
"build": "Build",
"build": "Gradnja",
"recipe-scraper-version": "Verzija strgalnika receptov",
"oidc-ready": "OIDC Ready",
"oidc-ready": "Pripravljeno za OIDC",
"oidc-not-ready": "OIDC ni pripravljen",
"oidc-ready-error-text": "Nekatere OIDC vrednosti niso nastavljene. To lahko ignoriraš, če ne uporabljaš OIDC avtentikacije.",
"oidc-ready-success-text": "Vse zahtevane OIDC spremenljivke so nastavljene.",
@@ -915,6 +917,7 @@
"quantity": "Količina: {0}",
"shopping-list": "Nakupovalni seznam",
"shopping-lists": "Nakupovalni seznami",
"add-item": "Dodaj element",
"food": "Živilo",
"note": "Opomba",
"label": "Oznaka",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Ali res želite izbrati vse elemente?",
"are-you-sure-you-want-to-uncheck-all-items": "Ali res ne želite izbrati vseh elementov?",
"are-you-sure-you-want-to-delete-checked-items": "Ali ste prepričani, da želite izbrisati vse izbrane elemente?",
"no-shopping-lists-found": "Ni nakupovalnih seznamov"
"no-shopping-lists-found": "Ni nakupovalnih seznamov",
"item-checked-off": "Odkljukano {item}"
},
"sidebar": {
"all-recipes": "Vsi recepti",
@@ -958,7 +962,7 @@
"language": "Jezik",
"maintenance": "Vzdrževanje",
"background-tasks": "Naloge v ozadju",
"parser": "Parser",
"parser": "Razčlenjevalnik",
"developer": "Razvijalec",
"cookbook": "Kuharska knjiga",
"create-cookbook": "Izdelaj novo kuharsko knjigo"
@@ -1082,7 +1086,7 @@
"authentication-method": "Izberi način avtentikacije",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"permissions": "Dovoljenja",
"administrator": "Administrator",
"administrator": "Skrbnik",
"user-can-invite-other-to-group": "Uporabnih lahko povabi druge v skupino",
"user-can-manage-group": "Uporabnik lahko upravlja s skupino",
"user-can-manage-household": "Uporabnik lahko upravlja gospodinjstvo",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Presledki niso dovoljeni",
"min-length": "Mora vsebovati vsaj {min} znakov",
"max-length": "Največ {max} znakov|Vsebuje lahko največ {max} znakov"
},
"announcements": {
"announcements": "Obvestila",
"all-announcements": "Vsa obvestila",
"mark-all-as-read": "Označi vse kot prebrano",
"show-announcements-from-mealie": "Prikaži obvestila od Mealie",
"show-announcements-setting-description": "Ali želite uporabnikom dovoliti ogled obvestil Mealie. Ko je to omogočeno, se lahko uporabniki v nastavitvah uporabnika še vedno odjavijo od njihovega prikaza"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Командна табла",
"delete": "Обриши",
"disabled": "Онемогућено",
"done": "Готово",
"download": "Преузми",
"duplicate": "Дуплирај",
"edit": "Измени",
@@ -168,10 +169,11 @@
"token": "Токен",
"tuesday": "уторак",
"type": "Тип",
"undo": "Undo",
"update": "Ажурирај",
"updated": "Ажурирано",
"upload": "Отпреми",
"url": "URL",
"url": "Url адреса",
"view": "Преглед",
"wednesday": "среда",
"yes": "Да",
@@ -424,15 +426,15 @@
"paprika-text": "Mealie може да увезе рецепте из апликације Paprika. Извезите ваше рецепте из Paprika апликације, промените екстензију извоза у .zip и отпремите је испод.",
"mealie-text": "Mealie може да увезе рецепте из Mealie апликације пре верзије 1.0. Извезите ваше рецепте са ваше старе инстанце и отпремите zip датотеку испод. Напомена: само рецепти се могу увести из овог извоза.",
"plantoeat": {
"title": "Plan to Eat",
"title": "Планирајте јело",
"description-long": "Mealie може да увезе рецепте из Plan to Eat."
},
"myrecipebox": {
"title": "My Recipe Box",
"title": "Моја кутија за рецепте",
"description-long": "Mealie може да увезе рецепте из My Recipe Box. Извезите ваше рецепте у CSV формату, затим отпремите .csv датотеку испод."
},
"recipekeeper": {
"title": "Recipe Keeper",
"title": "Чувар рецепата",
"description-long": "Mealie може да увезе рецепте из Recipe Keeper-а. Извезите ваше рецепте у zip формату, затим отпремите .zip датотеку испод."
}
},
@@ -441,7 +443,7 @@
"error-details": "Само веб-сајтови који садрже ld+json или микроподатке могу бити увезени од стране Mealieја. Већина великих сајтова са рецептима подржава ову структуру података. Ако ваш сајт не може бити увезен, али постоје json подаци у логу, молимо вас пошаљите github issue са URL-ом и подацима.",
"error-title": "Изгледа да нисмо могли ништа да пронађемо",
"from-url": "Увези рецепт",
"github-issues": "GitHub Issues",
"github-issues": "GitHub Problemi",
"google-ld-json-info": "Google ld+json информације",
"must-be-a-valid-url": "Мора бити валидан URL",
"paste-in-your-recipe-data-each-line-will-be-treated-as-an-item-in-a-list": "Налепите ваше податке о рецепту. Свака линија ће бити третирана као ставка на списку",
@@ -857,7 +859,7 @@
"webhooks": {
"test-webhooks": "Тестирај Webhooks",
"the-urls-listed-below-will-recieve-webhooks-containing-the-recipe-data-for-the-meal-plan-on-its-scheduled-day-currently-webhooks-will-execute-at": "URL-ови наведени у наставку ће примити webhook-ове који садрже податке о рецепту за план оброка у заказани дан. Тренутно ће се Webhook-ови извршавати у",
"webhook-url": "Webhook URL",
"webhook-url": "URL Webhook -а",
"webhooks-caps": "WEBHOOK-ОВИ",
"webhooks": "Webhooks",
"webhook-name": "Име Webhook-а",
@@ -915,6 +917,7 @@
"quantity": "Количина: {0}",
"shopping-list": "Списак за куповину",
"shopping-lists": "Спискови за куповину",
"add-item": "Додај ставку",
"food": "Храна",
"note": "Белешка",
"label": "Натпис",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Да ли сте сигурни да желите да означите све ставке?",
"are-you-sure-you-want-to-uncheck-all-items": "Да ли сте сигурни да желите да одзначите све ставке?",
"are-you-sure-you-want-to-delete-checked-items": "Да ли сте сигурни да желите да обришете све означене ставке?",
"no-shopping-lists-found": "Нису пронађени спискови за куповину"
"no-shopping-lists-found": "Нису пронађени спискови за куповину",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Сви рецепти",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Размак није дозвољен",
"min-length": "Мора бити најмање {min} карактера",
"max-length": "Мора бити највише {max} карактер|Мора бити највише {max} карактера"
},
"announcements": {
"announcements": "Најава",
"all-announcements": "Најава",
"mark-all-as-read": "Означи све као прочитано",
"show-announcements-from-mealie": "Најава",
"show-announcements-setting-description": "Да ли желите да дозволите корисницима да виде обавештења од Mealie-а. Када је омогућено, корисници и даље могу да се искључе из приказивања обавештења у својим корисничким подешавањима."
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Startsida",
"delete": "Ta bort",
"disabled": "Inaktiverad",
"done": "Done",
"download": "Ladda ner",
"duplicate": "Duplicera",
"edit": "Redigera",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Tisdag",
"type": "Typ",
"undo": "Ångra",
"update": "Uppdatera",
"updated": "Uppdaterad",
"upload": "Ladda upp",
@@ -331,8 +333,8 @@
"any-household": "Valfritt hushåll",
"no-meal-plan-defined-yet": "Ingen måltidsplan definierad ännu",
"no-meal-planned-for-today": "Ingen måltidsplan för idag",
"numberOfDaysPast-hint": "Number of days in the past on page load",
"numberOfDaysPast-label": "Default Days in the Past",
"numberOfDaysPast-hint": "Antal förflutna dagar vid sidhämtning",
"numberOfDaysPast-label": "Förvalda förflutna dagar",
"numberOfDays-hint": "Antal dagar vid sidhämtning",
"numberOfDays-label": "Förvalda dagar",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "Endast recept med dessa kategorier kommer att användas i måltidsplaner",
@@ -810,7 +812,7 @@
"settings-updated": "Inställningar uppdaterade",
"site-settings": "Systeminställningar",
"theme": {
"accent": "Accent",
"accent": "Accentfärg",
"dark": "Mörkt",
"default-to-system": "Standard",
"error": "Fel",
@@ -891,17 +893,17 @@
"server-side-base-url-error-text": "`BASE_URL` är fortfarande standardvärdet på API-servern. Detta kommer att orsaka problem med meddelanden som genereras på servern för e-postmeddelanden, etc.",
"server-side-base-url-success-text": "Serversidans URL matchar inte standard",
"ldap-ready": "LDAP Redo",
"ldap-not-ready": "LDAP Not Ready",
"ldap-not-ready": "LDAP ej tillgängligt",
"ldap-ready-error-text": "Alla LDAP-värden är inte konfigurerade. Detta kan ignoreras om du inte använder LDAP-autentisering.",
"ldap-ready-success-text": "Alla obligatoriska LDAP-variabler är satta.",
"build": "Bygge",
"recipe-scraper-version": "Version av Recept-scraper",
"oidc-ready": "OIDC Klar",
"oidc-not-ready": "OIDC Not Ready",
"oidc-not-ready": "OIDC ej tillgängligt",
"oidc-ready-error-text": "Alla OIDC-värden är inte konfigurerade. Detta kan ignoreras om du inte använder OIDC-autentisering.",
"oidc-ready-success-text": "Alla obligatoriska OIDC-variabler är satta.",
"openai-ready": "OpenAI redo",
"openai-not-ready": "OpenAI Not Ready",
"openai-not-ready": "OpenAI ej tillgängligt",
"openai-ready-error-text": "Alla OpenAI-värden är inte konfigurerade. Detta kan ignoreras om du inte använder OpenAI-funktioner.",
"openai-ready-success-text": "Alla obligatoriska OpenAI-variabler är satta."
},
@@ -915,6 +917,7 @@
"quantity": "Antal {0}",
"shopping-list": "Inköpslista",
"shopping-lists": "Inköpslistor",
"add-item": "Lägg till vara",
"food": "Mat",
"note": "Anteckning",
"label": "Etikett",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Är du säker på att du vill markera alla objekt?",
"are-you-sure-you-want-to-uncheck-all-items": "Är du säker på att du vill avmarkera alla objekt?",
"are-you-sure-you-want-to-delete-checked-items": "Är du säker på att du vill ta bort alla markerade objekt?",
"no-shopping-lists-found": "Inga inköpslistor hittades"
"no-shopping-lists-found": "Inga inköpslistor hittades",
"item-checked-off": "Kryssat av {item}"
},
"sidebar": {
"all-recipes": "Recept",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Ingen blanksteg tillåten",
"min-length": "Måste vara minst {min} tecken",
"max-length": "Måste Vara Som Mest {max} Tecken|Måste Vara Som Mest {max} Tecken"
},
"announcements": {
"announcements": "Meddelanden",
"all-announcements": "Alla meddelanden",
"mark-all-as-read": "Markera alla som lästa",
"show-announcements-from-mealie": "Visa meddelanden från Mealie",
"show-announcements-setting-description": "Om du vill tillåta användare att se meddelanden från Mealie eller inte. När funktionen är aktiverad kan användarna fortfarande välja att inte se dem i sina användarinställningar"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Pano",
"delete": "Sil",
"disabled": "Devre Dışı",
"done": "Done",
"download": "İndir",
"duplicate": "Çiftle",
"edit": "Düzenle",
@@ -168,6 +169,7 @@
"token": "Anahtar",
"tuesday": "Salı",
"type": "Tür",
"undo": "Undo",
"update": "Güncelle",
"updated": "Güncellendi",
"upload": "Yükle",
@@ -773,7 +775,7 @@
"backup-restore": "Yedekleme Geri Yükleme",
"back-restore-description": "Bu yedeği geri yüklemek, veritabanınızdaki ve veri dizinindeki tüm mevcut verilerin üzerine yazacak ve bunları bu yedeğin içeriğiyle değiştirecektir. {cannot-be-undone} Geri yükleme başarılı olursa oturumunuz kapatılacaktır.",
"cannot-be-undone": "Bu işlem geri alınamaz - dikkatli kullanın.",
"postgresql-note": "",
"postgresql-note": "If you are using PostgreSQL, please review the {backup-restore-process} prior to restoring.",
"backup-restore-process-in-the-documentation": "belgelerdeki yedekleme/geri yükleme işlemini",
"irreversible-acknowledgment": "Bu işlemin geri döndürülemez, yıkıcı ve veri kaybına neden olabileceğini anlıyorum",
"restore-backup": "Yedeği Geri Yükle"
@@ -915,6 +917,7 @@
"quantity": "Miktar: {0}",
"shopping-list": "Alışveriş Listesi",
"shopping-lists": "Alışveriş Listeleri",
"add-item": "Add item",
"food": "Gıda",
"note": "Not",
"label": "Etiket",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Tüm öğeleri işaretlemek istediğinizden emin misiniz?",
"are-you-sure-you-want-to-uncheck-all-items": "Tüm öğelerden işaretleri kaldırmak istediğinize emin misiniz?",
"are-you-sure-you-want-to-delete-checked-items": "İşaretlenmiş tüm öğeleri silmek istediğinizden emin misiniz?",
"no-shopping-lists-found": "Alışveriş Listesi Bulunamadı"
"no-shopping-lists-found": "Alışveriş Listesi Bulunamadı",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Tüm Tarifler",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "En Az {min} Karakter Olmalıdır",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Панель керування",
"delete": "Видалити",
"disabled": "Вимкнено",
"done": "Done",
"download": "Завантажити",
"duplicate": "Копіювати",
"edit": "Редагувати",
@@ -168,6 +169,7 @@
"token": "Токен",
"tuesday": "Вівторок",
"type": "Тип",
"undo": "Undo",
"update": "Оновити",
"updated": "Дата оновлення",
"upload": "Завантажити",
@@ -909,12 +911,13 @@
"all-lists": "Всі списки",
"create-shopping-list": "Сторити список покупок",
"from-recipe": "З рецепту",
"ingredient-of-recipe": "Ingredient of {recipe}",
"ingredient-of-recipe": "Інгредієнт з {recipe}",
"list-name": "Назва списку",
"new-list": "Новий список",
"quantity": "Кількість: {0}",
"shopping-list": "Список покупок",
"shopping-lists": "Списки покупок",
"add-item": "Add item",
"food": "Продукт",
"note": "Нотатка",
"label": "Мітка",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Ви впевнені, що хочете відмітити всі елементи?",
"are-you-sure-you-want-to-uncheck-all-items": "Ви впевнені, що хочете зняти відмітку з усіх елементів?",
"are-you-sure-you-want-to-delete-checked-items": "Ви впевнені, що хочете видалити всі відмічені елементи?",
"no-shopping-lists-found": "Списків покупок не знайдено"
"no-shopping-lists-found": "Списків покупок не знайдено",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "Всі рецепти",
@@ -1472,5 +1476,12 @@
"no-whitespace": "Пробіли заборонені",
"min-length": "Повинно бути не менше ніж {min} символів ",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "Bảng điều khiển",
"delete": "Xóa",
"disabled": "Đã vô hiệu",
"done": "Done",
"download": "Tải xuống",
"duplicate": "Tạo bản sao",
"edit": "Chỉnh sửa",
@@ -168,6 +169,7 @@
"token": "Token",
"tuesday": "Thứ 3",
"type": "Loại",
"undo": "Undo",
"update": "Cập nhật",
"updated": "Đã cập nhật",
"upload": "Tải lên",
@@ -915,6 +917,7 @@
"quantity": "Quantity: {0}",
"shopping-list": "Shopping List",
"shopping-lists": "Shopping Lists",
"add-item": "Add item",
"food": "Food",
"note": "Note",
"label": "Label",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "Are you sure you want to check all items?",
"are-you-sure-you-want-to-uncheck-all-items": "Are you sure you want to uncheck all items?",
"are-you-sure-you-want-to-delete-checked-items": "Are you sure you want to delete all checked items?",
"no-shopping-lists-found": "No Shopping Lists Found"
"no-shopping-lists-found": "No Shopping Lists Found",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "All Recipes",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "仪表盘",
"delete": "删除",
"disabled": "已禁用",
"done": "Done",
"download": "下载",
"duplicate": "创建副本",
"edit": "编辑",
@@ -168,6 +169,7 @@
"token": "密钥",
"tuesday": "周二",
"type": "类型",
"undo": "Undo",
"update": "更新",
"updated": "已更新",
"upload": "上传",
@@ -915,6 +917,7 @@
"quantity": "数量: {0} 个",
"shopping-list": "购物清单",
"shopping-lists": "购物清单",
"add-item": "Add item",
"food": "食品",
"note": "备注",
"label": "标注",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "您确定要选择所有项目吗?",
"are-you-sure-you-want-to-uncheck-all-items": "您确定要取消选中所有项目吗?",
"are-you-sure-you-want-to-delete-checked-items": "您确定要删除所有选中的项目吗?",
"no-shopping-lists-found": "没有发现购物清单"
"no-shopping-lists-found": "没有发现购物清单",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "全部食谱",
@@ -1472,5 +1476,12 @@
"no-whitespace": "No Whitespace Allowed",
"min-length": "Must Be At Least {min} Characters",
"max-length": "Must Be At Most {max} Character|Must Be At Most {max} Characters"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -98,6 +98,7 @@
"dashboard": "控制面板",
"delete": "刪除",
"disabled": "已停用",
"done": "Done",
"download": "下載",
"duplicate": "複製",
"edit": "編輯",
@@ -168,6 +169,7 @@
"token": "金鑰",
"tuesday": "星期二",
"type": "類型",
"undo": "Undo",
"update": "更新",
"updated": "已更新",
"upload": "上傳",
@@ -915,6 +917,7 @@
"quantity": "數量:{0}",
"shopping-list": "購物清單",
"shopping-lists": "購物清單",
"add-item": "Add item",
"food": "食材",
"note": "備註",
"label": "標籤",
@@ -939,7 +942,8 @@
"are-you-sure-you-want-to-check-all-items": "確定要勾選所有項目嗎?",
"are-you-sure-you-want-to-uncheck-all-items": "確定要取消勾選所有項目嗎?",
"are-you-sure-you-want-to-delete-checked-items": "確定要刪除所有已勾選的項目嗎?",
"no-shopping-lists-found": "找不到購物清單"
"no-shopping-lists-found": "找不到購物清單",
"item-checked-off": "Checked off {item}"
},
"sidebar": {
"all-recipes": "所有食譜",
@@ -1472,5 +1476,12 @@
"no-whitespace": "不允許空白字元",
"min-length": "至少需 {min} 個字元",
"max-length": "最多 {max} 個字元|最多 {max} 個字元"
},
"announcements": {
"announcements": "Announcements",
"all-announcements": "All announcements",
"mark-all-as-read": "Mark All as Read",
"show-announcements-from-mealie": "Show announcements from Mealie",
"show-announcements-setting-description": "Whether or not you want to allow users to see announcements from Mealie. When enabled users can still opt-out from seeing them in their user settings"
}
}

View File

@@ -3,8 +3,11 @@ export default defineNuxtRouteMiddleware((to) => {
const { user } = useMealieAuth();
const groupSlug = user.value?.groupSlug;
if (!groupSlug) {
return navigateTo("/login", { redirectCode: 301 });
// Preserve the full path (including recipe_import_url query param) so the
// login page can redirect back here after successful authentication.
const redirect = encodeURIComponent(to.fullPath);
return navigateTo(`/login?redirect=${redirect}`, { redirectCode: 302 });
}
return navigateTo(`/g/${groupSlug}${to.fullPath}`, { redirectCode: 301 });
return navigateTo(`/g/${groupSlug}${to.fullPath}`, { redirectCode: 302 });
}
});

View File

@@ -14,7 +14,7 @@
</v-card-title>
<BaseDivider />
<v-card-text>
<v-form @submit.prevent="requestLink()">
<v-form ref="form" @submit.prevent="requestLink()">
<v-text-field
v-model="state.email"
:prepend-inner-icon="$globals.icons.email"
@@ -24,6 +24,7 @@
name="login"
:label="$t('user.email')"
type="text"
:rules="[validators.email]"
/>
<p class="text-center">
{{ $t('user.forgot-password-text') }}
@@ -63,11 +64,15 @@
<script setup lang="ts">
import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import { validators } from "~/composables/use-validators";
import type { VForm } from "~/types/auto-forms";
definePageMeta({
layout: "basic",
});
const form = ref<VForm | null>(null);
const state = reactive({
email: "",
loading: false,
@@ -84,17 +89,27 @@ useSeoMeta({
const api = useUserApi();
async function requestLink() {
if (!form.value) {
return;
};
const { valid } = await form.value.validate();
if (!valid) {
return;
};
state.loading = true;
// TODO: Fix Response to send meaningful error
const { response } = await api.email.sendForgotPassword({ email: state.email });
state.loading = false;
if (response?.status === 200) {
state.loading = false;
state.error = false;
alert.success(i18n.t("profile.email-sent"));
await navigateTo("/login");
}
else {
state.loading = false;
state.error = true;
alert.error(i18n.t("profile.error-sending-email"));
}

View File

@@ -198,15 +198,32 @@ const recipeUrl = computed({
}
},
get() {
return route.query.recipe_import_url as string | null;
// Prefer the 'url' share field (recipe_import_url, populated by Chrome when
// sharing a page URL). Fall back to the 'text' share field (recipe_import_text)
// for apps that share URLs as plain text, but only when the text value is
// actually a valid http/https URL — shared text can be arbitrary.
const urlFromField = route.query.recipe_import_url as string | null;
if (urlFromField) {
return urlFromField;
}
const textFromField = route.query.recipe_import_text as string | null;
if (textFromField) {
try {
const parsed = new URL(textFromField);
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
return textFromField;
}
}
catch { /* not a URL, ignore */ }
}
return null;
},
});
onMounted(() => {
if (recipeUrl.value && recipeUrl.value.includes("https")) {
// Check if we have a query params for using keywords as tags or staying in edit mode.
// We don't use these in the app anymore, but older automations such as Bookmarklet might still use them,
// and they're easy enough to support.
if (recipeUrl.value) {
// Apply legacy query params for older automations such as the Bookmarklet.
// These are no longer used by the app itself but are easy to keep supporting.
const importKeywordsAsTagsParam = route.query.use_keywords;
if (importKeywordsAsTagsParam === "1") {
importKeywordsAsTags.value = true;
@@ -223,8 +240,9 @@ onMounted(() => {
stayInEditMode.value = false;
}
createByUrl(recipeUrl.value, importKeywordsAsTags.value, false);
return;
// The URL is pre-filled via the recipeUrl computed property.
// Do not auto-submit: the user should review the import options and
// confirm by clicking the submit button.
}
});

View File

@@ -1,8 +1,8 @@
<template>
<div>
<GroupDataPage
:icon="$globals.icons.linkVariantPlus"
:title="$t('data-pages.recipe-actions.new-recipe-action')"
:icon="$globals.icons.link"
:title="$t('data-pages.recipe-actions.recipe-actions-data')"
:create-title="$t('data-pages.recipe-actions.new-recipe-action')"
:edit-title="$t('data-pages.recipe-actions.edit-recipe-action')"
:table-headers="tableHeaders"

View File

@@ -180,12 +180,20 @@
}}
</p>
</v-card-actions>
<div class="mx-2 clip-width">
<v-text-field
v-model="search"
variant="underlined"
:label="$t('search.search')"
/>
</div>
<v-card>
<RecipeDataTable
v-model="selected"
:loading="loading"
:recipes="allRecipes"
:show-headers="headers"
:search="search"
/>
<v-card-actions class="justify-end">
<BaseButton
@@ -263,6 +271,7 @@ useSeoMeta({
const { refreshRecipes } = useRecipes(true, true, false, `householdId=${auth.user.value?.householdId || ""}`);
const selected = ref<Recipe[]>([]);
const search = ref("");
function resetAll() {
selected.value = [];
@@ -513,6 +522,9 @@ const selectedOwnerHousehold = computed(() => {
</script>
<style>
.clip-width {
max-width: 400px;
}
.v-btn--disabled {
opacity: 0.5 !important;
}

View File

@@ -211,7 +211,7 @@
</template>
<script setup lang="ts">
import { useDark, whenever } from "@vueuse/core";
import { useDark, useSessionStorage, whenever } from "@vueuse/core";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import { usePasswordField } from "~/composables/use-passwords";
import { alert } from "~/composables/use-toast";
@@ -225,6 +225,7 @@ definePageMeta({
const isDark = useDark();
const router = useRouter();
const route = useRoute();
const i18n = useI18n();
const auth = useMealieAuth();
const { $appInfo, $axios } = useNuxtApp();
@@ -235,6 +236,9 @@ const isFirstLogin = ref(false);
const activityPreferences = useUserActivityPreferences();
const { getDefaultActivityRoute } = useDefaultActivity();
// Survives the page reload that happens during OIDC redirect
const pendingShareRedirect = useSessionStorage<string | null>("pwa_share_redirect", null);
useSeoMeta({
title: i18n.t("user.login"),
});
@@ -259,14 +263,29 @@ useAsyncData(useAsyncKey(), async () => {
whenever(
() => loggedIn.value && groupSlug.value,
() => {
// First-login setup always takes priority
if (!isDemo.value && isFirstLogin.value && auth.user.value?.admin) {
router.push("/admin/setup");
return;
}
// After login, honour a pending PWA share redirect.
// The redirect param can arrive via the URL query string (password login)
// or via sessionStorage (OIDC login, where the OIDC provider reload clears
// the query string).
const redirectFromQuery = route.query.redirect as string | undefined;
const redirectTarget = redirectFromQuery ?? pendingShareRedirect.value;
if (redirectTarget && redirectTarget.startsWith("/")) {
pendingShareRedirect.value = null;
router.push(redirectTarget);
return;
}
const defaultActivityRoute = getDefaultActivityRoute(
activityPreferences.value.defaultActivity,
groupSlug.value,
);
if (!isDemo.value && isFirstLogin.value && auth.user.value?.admin) {
router.push("/admin/setup");
}
else if (defaultActivityRoute) {
if (defaultActivityRoute) {
router.push(defaultActivityRoute);
}
else {
@@ -316,6 +335,13 @@ async function oidcAuthenticate(callback = false) {
oidcLoggingIn.value = false;
}
else {
// Save any pending PWA share redirect before leaving for the OIDC provider.
// The OIDC callback reloads the page, which clears the query string, so we
// persist the target in sessionStorage and restore it after login.
const redirectTarget = route.query.redirect as string | undefined;
if (redirectTarget && redirectTarget.startsWith("/")) {
pendingShareRedirect.value = redirectTarget;
}
navigateTo("/api/auth/oauth", { external: true }); // start the redirect process
}
}

View File

@@ -14,7 +14,7 @@
</v-card-title>
<BaseDivider />
<v-card-text>
<v-form @submit.prevent="requestLink()">
<v-form ref="form" @submit.prevent="requestLink()">
<v-text-field
v-model="state.email"
:prepend-inner-icon="$globals.icons.email"
@@ -86,6 +86,7 @@ import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import { validators } from "@/composables/use-validators";
import { useRouteQuery } from "~/composables/use-router";
import type { VForm } from "~/types/auto-forms";
definePageMeta({
layout: "basic",
@@ -99,6 +100,8 @@ const state = reactive({
error: false,
});
const form = ref<VForm | null>(null);
const i18n = useI18n();
const passwordMatch = () => state.password === state.passwordConfirm || i18n.t("user.password-must-match");
@@ -115,6 +118,15 @@ const token = useRouteQuery("token", "");
// API
const api = useUserApi();
async function requestLink() {
if (!form.value) {
return;
};
const { valid } = await form.value.validate();
if (!valid) {
return;
};
state.loading = true;
// TODO: Fix Response to send meaningful error
const { response } = await api.users.resetPassword({
@@ -127,12 +139,11 @@ async function requestLink() {
state.loading = false;
if (response?.status === 200) {
state.loading = false;
state.error = false;
alert.success(i18n.t("user.password-updated"));
await navigateTo("/login");
}
else {
state.loading = false;
state.error = true;
alert.error(i18n.t("events.something-went-wrong"));
}

View File

@@ -160,8 +160,20 @@
<!-- Viewer -->
<section v-if="!edit" class="py-2 d-flex flex-column ga-4">
<!-- Create Item -->
<div v-if="createEditorOpen">
<ShoppingListAddItemForm
v-if="$vuetify.display.smAndDown"
v-model="createListItemData"
class="my-4"
:labels="allLabels || []"
:units="allUnits || []"
:foods="allFoods || []"
@cancel="createEditorOpen = false"
@save="createListItem"
/>
<div v-else>
<ShoppingListItemEditor
v-if="createEditorOpen"
v-model="createListItemData"
class="my-4"
:labels="allLabels || []"
@@ -172,14 +184,14 @@
@cancel="createEditorOpen = false"
@save="createListItem"
/>
</div>
<div v-else class="d-flex justify-end">
<BaseButton
create
@click="createEditorOpen = true"
>
{{ $t('general.add') }}
</BaseButton>
<InputLabelType
v-else
:items="allFoods"
:label="$t('shopping-list.add-item')"
:icon="$globals.icons.foods"
search
@focus="createEditorOpen = true"
/>
</div>
<TransitionGroup name="scroll-x-transition">
@@ -211,7 +223,10 @@
:units="allUnits || []"
:foods="allFoods || []"
:recipes="recipeMap"
@checked="saveListItem"
@checked="(item) => {
saveListItem(item);
itemCheckedToast(item);
}"
@save="saveListItem"
@delete="deleteListItem(item)"
/>
@@ -338,10 +353,13 @@
import { VueDraggable } from "vue-draggable-plus";
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
import MultiPurposeLabelSection from "~/components/Domain/ShoppingList/MultiPurposeLabelSection.vue";
import ShoppingListAddItemForm from "~/components/Domain/ShoppingList/ShoppingListAddItemForm.vue";
import ShoppingListItem from "~/components/Domain/ShoppingList/ShoppingListItem.vue";
import ShoppingListItemEditor from "~/components/Domain/ShoppingList/ShoppingListItemEditor.vue";
import { useShoppingListPage } from "~/composables/shopping-list-page/use-shopping-list-page";
import { useFoodStore, useLabelStore, useUnitStore } from "~/composables/store";
import { useLabelStore, useUnitStore, useFoodStore } from "~/composables/store";
import { alert } from "~/composables/use-toast";
import type { ShoppingListItemOut } from "~/lib/api/types/household";
const { mdAndUp } = useDisplay();
const i18n = useI18n();
@@ -358,6 +376,25 @@ const { store: allLabels } = useLabelStore();
const { store: allUnits } = useUnitStore();
const { store: allFoods } = useFoodStore();
function itemCheckedToast(item: ShoppingListItemOut) {
setTimeout(() => {
alert.info(
i18n.t("shopping-list.item-checked-off", { item: item.food?.name || item.note || i18n.t("recipe.ingredient") }),
undefined,
{
timeout: 4000,
action: {
message: i18n.t("general.undo"),
onClick: () => {
item.checked = false;
shoppingListPage.saveListItem(item);
},
},
},
);
}, 500);
}
const {
shoppingList,
state,

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