Compare commits

...

66 Commits

Author SHA1 Message Date
mealie-commit-bot[bot]
4101797c0e chore: bump version to v3.10.2 2026-02-04 23:32:41 +00:00
Michael Genson
6110200a04 fix: OIDC caching (#7009) 2026-02-04 14:03:40 -06:00
renovate[bot]
49f1e76776 fix(deps): update dependency fastapi to v0.128.1 (#7008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 19:20:25 +00:00
Hayden
24e9417d02 chore(l10n): New Crowdin updates (#7005) 2026-02-04 11:57:13 +00:00
renovate[bot]
69d6985f3b chore(deps): update node.js to 1de022d (#7002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 00:23:07 +00:00
renovate[bot]
84cdeb2398 chore(deps): update dependency coverage to v7.13.3 (#6998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 00:23:00 +00:00
Hayden
6d439de144 chore(l10n): New Crowdin updates (#7004) 2026-02-03 23:33:27 +00:00
Michael Genson
1b586f8c67 chore: Upgrade to ruff 15.0.0 (#7003) 2026-02-03 16:43:42 -06:00
whattheschnell
f82f387146 fix: use BASE_URL config for redirect_url if available (#6995)
Co-authored-by: Michael Genson <genson.michael@gmail.com>
2026-02-03 16:31:20 -06:00
Hayden
d31c07a6c5 chore(l10n): New Crowdin updates (#6997) 2026-02-03 14:15:18 +00:00
renovate[bot]
84372c2f4f chore(deps): update node.js to bdc7252 (#6996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-03 14:15:03 +00:00
mealie-commit-bot[bot]
168ac79daa chore: bump version to v3.10.1 2026-02-03 01:04:49 +00:00
Hayden
22296277a8 chore(l10n): New Crowdin updates (#6994) 2026-02-03 00:51:49 +00:00
Michael Genson
6e006458be fix: Button overflow on main page filters (#6992) 2026-02-02 18:44:36 -06:00
Michael Genson
76a2fea076 docs: Typo (#6993) 2026-02-02 16:20:41 -06:00
mealie-commit-bot[bot]
3de4024619 chore: bump version to v3.10.0 2026-02-02 18:33:02 +00:00
renovate[bot]
194771653d chore(deps): pin dependency freezegun to ==1.5.5 (#6991)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-02 18:18:36 +00:00
renovate[bot]
24aa8f3525 fix(deps): update dependency orjson to v3.11.7 (#6989)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-02 17:54:33 +00:00
Michael Genson
fb8e318739 fix: Flaky $NOW tests (#6990) 2026-02-02 11:24:13 -06:00
Michael Genson
6255c71609 docs: Misc. cleanup (#6988) 2026-02-02 10:47:23 -06:00
Hayden
f2d1569488 chore(l10n): New Crowdin updates (#6987) 2026-02-02 11:18:56 +00:00
Michael Genson
987c7209fc feat: Query relative dates (#6984) 2026-02-01 21:36:46 -06:00
Hayden
f6dbd1f1f1 chore(l10n): New Crowdin updates (#6983) 2026-02-01 23:23:28 +00:00
Michael Genson
d30118899d fix: Re-enable some style tags (#6982) 2026-02-01 16:24:57 -06:00
Michael Genson
af241dad57 feat: Add range of dates to shopping list from meal planner (#6981) 2026-02-01 15:58:03 -06:00
renovate[bot]
b86de79c6f chore(deps): update dependency rich to v14.3.2 (#6980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-01 20:28:33 +00:00
Hayden
86e86f8c81 chore(l10n): New Crowdin updates (#6979) 2026-02-01 10:58:51 +00:00
mealie-actions[bot]
d795f91938 chore(l10n): Crowdin locale sync (#6977)
Co-authored-by: GitHub Action <action@github.com>
2026-02-01 04:42:54 +00:00
Michael Genson
a59511cc81 dev: Switch to Mealie bot for auto-merging (#6978) 2026-01-31 22:42:03 -06:00
Michael Genson
a5d4cae6d0 dev: Switch approver for automated i18n merges (#6976) 2026-01-31 21:46:13 -06:00
Michael Genson
2987cf8ba6 dev: Allow locale sync path in auto merge (#6974) 2026-01-31 21:37:31 -06:00
Michael Genson
46b46978ff dev: Increase locale merge limit to 300 and allow PRs from Mealie bot (#6972) 2026-01-31 21:31:52 -06:00
Michael Genson
12857883a9 dev: Fix token vars (#6970) 2026-01-31 21:24:58 -06:00
Michael Genson
60fff3b5b8 dev: Switch to bot token for locale sync (#6969) 2026-01-31 21:21:07 -06:00
Hayden
b42e888929 chore(l10n): New Crowdin updates (#6967) 2026-02-01 00:47:50 +00:00
Michael Genson
570d6f1433 feat: Migrate OpenAI implementation to use structured outputs (#6964) 2026-01-31 11:57:05 -06:00
Morgan
dcf410739e fix: service-worker precache manifest entries not generated correctly (#6815) 2026-01-31 15:51:11 +00:00
Michael Genson
1929d630a1 fix: Remove deprecated warning from shopping list editor (#6963) 2026-01-31 09:44:34 -06:00
Michael Genson
c4c7bf2aed fix: Disable context hover (#6962) 2026-01-31 09:40:31 -06:00
Hayden
47034d18c5 chore(l10n): New Crowdin updates (#6960) 2026-01-31 15:09:56 +00:00
renovate[bot]
7ebe491f74 fix(deps): update dependency ingredient-parser-nlp to v2.5.0 (#6961)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-31 14:52:15 +00:00
Arsène Reymond
719bd89eb1 feat: Improve recipe assets preview (#6602)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-31 14:48:42 +00:00
Hayden
9030c7e6b9 chore(l10n): New Crowdin updates (#6959) 2026-01-30 23:15:01 +00:00
renovate[bot]
0202cc7ef8 fix(deps): update dependency pyjwt to v2.11.0 (#6958)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-30 20:53:19 +00:00
Patrick Lehner (he/him)
381ac9bfde dev: Improve caching in taskfile (#6798) 2026-01-30 20:52:44 +00:00
Patrick Lehner (he/him)
e9fe71c1b7 dev: Add tasks for e2e tests (#6797)
Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-30 14:51:40 -06:00
Arsène Reymond
79bbc20cd6 fix: recipe context menu (#6782) 2026-01-30 20:08:49 +00:00
Arsène Reymond
c7be4a452a fix: disable invitations when password login is disabled (#6781) 2026-01-30 20:05:40 +00:00
Imanuel
731ee8ae3d fix: sub-recipes in multi group setup (#6652) (#6663) 2026-01-30 18:50:08 +00:00
Imanuel
c7ae67e7cd feat: Customizable OpenAI prompts (#5146) (#6588)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Michael Genson <genson.michael@gmail.com>
2026-01-30 12:00:03 -06:00
Gtt1229
e83891e3ca feat: Added Option to Import Recipe Category During Recipe Import (#6523)
Co-authored-by: Michael Genson <genson.michael@gmail.com>
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-30 11:18:15 -06:00
Michael Genson
e3e45c534e dev: Skip Trivy on merge queue (#6957) 2026-01-30 16:47:30 +00:00
Stevie Howard
279cf65673 fix: Seed data - en-US only - correct [some] plural names and add [some] accented characters (#6405)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-30 16:30:56 +00:00
Fjodor42
cb44ecf394 feat: Add "sprig" as a unit. (#6934)
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2026-01-30 16:03:41 +00:00
Michael Genson
920eeb26d6 dev: Bunch of GH workflow fixes (#6956) 2026-01-30 15:58:49 +00:00
Hayden
9738d9f363 fix: dispose AlchemyExporter engine after restore completes (#6942) 2026-01-30 15:54:38 +00:00
CodeFaux
37e6123f9e fix: Keep ingredient headers in cook mode (#6946)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-30 15:36:04 +00:00
Hayden
0a2cabb348 chore(l10n): New Crowdin updates (#6954) 2026-01-30 15:19:02 +00:00
Michael Genson
447a1fb239 dev: Enable CI on merge queues (#6955) 2026-01-30 09:18:52 -06:00
Hayden
b5358896eb fix: use GITHUB_TOKEN for auto-merge to respect CI checks (#6953) 2026-01-30 03:26:10 +00:00
Jérôme
78fbbf0264 fix: correct global scroll strategy to prevent menu fixation (#6577)
Co-authored-by: Jerome <jerome.roth@imt-atlantique.net>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
2026-01-30 02:27:45 +00:00
Hayden
a33d8204df chore(l10n): New Crowdin updates (#6949)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-30 02:10:24 +00:00
Hayden
c8046bbdf0 chore: add workflow to auto-merge l10n PRs (#6948)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
2026-01-30 02:05:56 +00:00
renovate[bot]
329ad4d8ed fix(deps): update dependency alembic to v1.18.3 (#6945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-30 00:43:47 +00:00
renovate[bot]
4ccf649aa1 chore(deps): update dependency setuptools to v80.10.2 (#6930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-29 18:32:44 -06:00
renovate[bot]
5994328a8b fix(deps): update dependency orjson to v3.11.6 (#6952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-29 13:42:53 -06:00
233 changed files with 50674 additions and 49001 deletions

View File

@@ -1,9 +1,10 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.224.2/containers/python-3/.devcontainer/base.Dockerfile
# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
ARG VARIANT="3.12-bullseye"
FROM mcr.microsoft.com/devcontainers/python:${VARIANT}
# Remove outdated yarn GPG key, if it exists
RUN rm -f /etc/apt/sources.list.d/yarn.list /usr/share/keyrings/yarn-archive-keyring.gpg || true
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi

113
.github/workflows/auto-merge-l10n.yml vendored Normal file
View File

@@ -0,0 +1,113 @@
name: Auto-merge l10n PRs
on:
pull_request:
types: [opened, synchronize, labeled]
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'l10n')
steps:
- name: Validate PR author
env:
AUTHOR: ${{ github.event.pull_request.user.login }}
run: |
if [[
"$AUTHOR" != "hay-kot" &&
"$AUTHOR" != "github-actions[bot]" &&
"$AUTHOR" != "mealie-actions[bot]"
]]; then
echo "::error::PR author must be hay-kot, github-actions[bot], or mealie-actions[bot] for auto-merge (got: $AUTHOR)"
exit 1
fi
echo "Author validated: $AUTHOR"
- name: Validate PR size
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
ADDITIONS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json additions --jq '.additions')
DELETIONS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json deletions --jq '.deletions')
TOTAL=$((ADDITIONS + DELETIONS))
echo "PR changes: +$ADDITIONS -$DELETIONS (total: $TOTAL lines)"
if [ "$TOTAL" -gt 400 ]; then
echo "::error::PR exceeds 400 line change limit ($TOTAL lines)"
exit 1
fi
- name: Validate file paths
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
FILES=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json files --jq '.files[].path')
for file in $FILES; do
# Check if file matches any allowed path
if [[ "$file" == "frontend/composables/use-locales/available-locales.ts" ]] || \
[[ "$file" =~ ^frontend/lang/ ]] || \
[[ "$file" =~ ^mealie/repos/seed/resources/[^/]+/locales/ ]]; then
continue
fi
# File doesn't match allowed paths
echo "::error::Invalid file path: $file"
echo "Only the following paths are allowed:"
echo " - frontend/composables/use-locales/available-locales.ts"
echo " - frontend/lang/"
echo " - mealie/repos/seed/resources/*/locales/"
exit 1
done
echo "All files are in allowed paths"
- name: Approve PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
APPROVED=$(gh pr view "$PR_NUMBER" \
--repo "$REPO" \
--json reviews \
--jq '.reviews[] | select(.state == "APPROVED") | .id' \
| wc -l)
if [ "$APPROVED" -gt 0 ]; then
echo "PR already approved"
exit 0
fi
gh pr review "$PR_NUMBER" \
--repo "$REPO" \
--approve \
--body "Auto-approved: l10n PR from trusted author with valid file paths"
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.COMMIT_BOT_APP_ID }}
private-key: ${{ secrets.COMMIT_BOT_APP_PRIVATE_KEY }}
- name: Enable auto-merge
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
gh pr merge "$PR_NUMBER" \
--repo "$REPO" \
--auto \
--squash

View File

@@ -15,10 +15,17 @@ jobs:
sync-locales:
runs-on: ubuntu-latest
steps:
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.COMMIT_BOT_APP_ID }}
private-key: ${{ secrets.COMMIT_BOT_APP_PRIVATE_KEY }}
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ steps.app-token.outputs.token }}
- name: Set up Python
uses: actions/setup-python@v5
@@ -105,7 +112,7 @@ jobs:
- Updated frontend locale files
- Generated from latest translation sources" \
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
- name: No changes detected
if: steps.changes.outputs.has_changes == 'false'

View File

@@ -4,14 +4,19 @@ on:
pull_request:
branches:
- mealie-next
merge_group:
types: [checks_requested]
branches:
- mealie-next
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_ref }}
cancel-in-progress: true
jobs:
pull-request-lint:
name: "Lint PR"
if: github.event_name == 'pull_request'
uses: ./.github/workflows/pull-request-lint.yml
backend-tests:
@@ -24,6 +29,7 @@ jobs:
container-scanning:
name: "Trivy Container Scanning"
if: github.event_name == 'pull_request'
uses: ./.github/workflows/partial-trivy-container-scanning.yml
code-ql:
@@ -47,7 +53,10 @@ jobs:
publish-image:
name: "Publish PR Image"
if: contains(github.event.pull_request.labels.*.name, 'build-image') && github.repository == 'mealie-recipes/mealie'
if: |
github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'build-image') &&
github.repository == 'mealie-recipes/mealie'
permissions:
contents: read
packages: write

View File

@@ -40,12 +40,18 @@ jobs:
shell: bash
run: pre-commit autoupdate --color=always
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
# This doesn't currently work for us because it creates the PR but the workflows don't run.
# TODO: Provide a personal access token as a parameter here, that solves that problem.
# https://github.com/peter-evans/create-pull-request
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.COMMIT_BOT_APP_ID }}
private-key: ${{ secrets.COMMIT_BOT_APP_PRIVATE_KEY }}
- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v6
with:
token: ${{ steps.app-token.outputs.token }}
commit-message: "Update pre-commit hooks"
branch: "fix/update-pre-commit-hooks"
labels: |
@@ -54,3 +60,38 @@ jobs:
base: mealie-next
title: "chore(auto): Update pre-commit hooks"
body: "Auto-generated by `.github/workflows/scheduled-checks.yml`"
- name: Approve PR
if: steps.create-pr.outputs.pull-request-number
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }}
REPO: ${{ github.repository }}
run: |
APPROVED=$(gh pr view "$PR_NUMBER" \
--repo "$REPO" \
--json reviews \
--jq '.reviews[] | select(.state == "APPROVED") | .id' \
| wc -l)
if [ "$APPROVED" -gt 0 ]; then
echo "PR already approved"
exit 0
fi
gh pr review "$PR_NUMBER" \
--repo "$REPO" \
--approve \
--body "Auto-approved: Pre-commit hook updates"
- name: Enable auto-merge
if: steps.create-pr.outputs.pull-request-number
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }}
REPO: ${{ github.repository }}
run: |
gh pr merge "$PR_NUMBER" \
--repo "$REPO" \
--auto \
--squash

View File

@@ -12,7 +12,7 @@ repos:
exclude: ^tests/data/
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.14
rev: v0.15.0
hooks:
- id: ruff
- id: ruff-format

View File

@@ -47,8 +47,6 @@ tasks:
sources:
- package.json
- yarn.lock
generates:
- node_modules/**
setup:py:
desc: setup python dependencies
@@ -61,6 +59,18 @@ tasks:
- pyproject.toml
- .pre-commit-config.yaml
setup:e2e:
desc: setup e2e test dependencies
dir: tests/e2e
run: once
cmds:
- yarn install
- yarn playwright install --with-deps
sources:
- package.json
- playwright.config.ts
- yarn.lock
setup:
desc: setup all dependencies
deps:
@@ -179,12 +189,21 @@ tasks:
status:
- '{{ .SKIP_PACKAGE_DEPS | default "false"}}'
py:package:
desc: builds Python packages (sdist and wheel) in top-level dist directory
py:package:build:
internal: true
deps:
- py:package:deps
cmds:
- uv build --out-dir dist
sources:
- uv.lock
- pyproject.toml
- mealie/**
py:package:
desc: builds Python packages (sdist and wheel) in top-level dist directory
cmds:
- task: py:package:build
- task: py:package:generate-requirements
py:
@@ -215,6 +234,12 @@ tasks:
dir: frontend
cmds:
- yarn build
sources:
- "**"
- exclude: .nuxt/**
- exclude: .output/**
- exclude: dist/**
- exclude: node_modules/.cache/**
ui:generate:
desc: generates a static version of the frontend in frontend/dist
@@ -223,18 +248,36 @@ tasks:
- setup:ui
cmds:
- yarn generate
sources:
- "**"
- exclude: .nuxt/**
- exclude: .output/**
- exclude: dist/**
- exclude: node_modules/.cache/**
ui:lint:
desc: runs the frontend linter
dir: frontend
cmds:
- yarn lint --max-warnings=0
sources:
- "**"
- exclude: .nuxt/**
- exclude: .output/**
- exclude: dist/**
- exclude: node_modules/.cache/**
ui:test:
desc: runs the frontend tests
dir: frontend
cmds:
- yarn test
sources:
- "**"
- exclude: .nuxt/**
- exclude: .output/**
- exclude: dist/**
- exclude: node_modules/.cache/**
ui:check:
desc: runs all frontend checks
@@ -263,3 +306,48 @@ tasks:
dir: docker
cmds:
- docker compose -f docker-compose.yml -p mealie up -d --build
e2e:build-image:
desc: builds the e2e test docker image
deps:
- py:package
cmds:
- docker build --tag mealie:e2e --file docker/Dockerfile --build-context packages=dist .
sources:
- docker/Dockerfile
- dist/**
e2e:start-server:
desc: Builds the image and starts the containers for e2e testing
dir: tests/e2e/docker
deps:
- e2e:build-image
vars:
WAIT_UNTIL_HEALTHY: '{{if .WAIT_UNTIL_HEALTHY}}--wait{{else}}{{end}}'
cmds:
- docker compose up -d {{.WAIT_UNTIL_HEALTHY}}
e2e:stop-server:
desc: Shuts down the e2e testing containers
dir: tests/e2e/docker
cmds:
- docker compose down --volumes
e2e:test:
desc: runs the e2e tests
dir: tests/e2e
deps:
- setup:e2e
vars:
PREVENT_REPORT_OPEN: '{{if .PREVENT_REPORT_OPEN}}PLAYWRIGHT_HTML_OPEN=never{{else}}{{end}}'
cmds:
- '{{.PREVENT_REPORT_OPEN}} yarn playwright test'
e2e:
desc: runs the full e2e test suite
cmds:
- task: e2e:start-server
vars: { WAIT_UNTIL_HEALTHY: true }
- defer: { task: e2e:stop-server }
- task: e2e:test
vars: { PREVENT_REPORT_OPEN: true }

View File

@@ -1,7 +1,7 @@
###############################################
# Frontend Build
###############################################
FROM node:24@sha256:b2b2184ba9b78c022e1d6a7924ec6fba577adf28f15c9d9c457730cc4ad3807a \
FROM node:24@sha256:1de022d8459f896fff2e7b865823699dc7a8d5567507e8b87b14a7442e07f206 \
AS frontend-builder
WORKDIR /frontend

View File

@@ -6,7 +6,7 @@ While this guide aims to simplify the migration process for developers, it's not
## V1 → V2
The biggest change between V1 and V2 is the introduction of Households. For more information on how households work in relation to groups/users, check out the [Groups and Households](./features.md#groups-and-households) section in the Features guide.
The biggest change between V1 and V2 is the introduction of Households. For more information on how households work in relation to groups/users, check out the [Groups and Households](../../documentation/getting-started/features.md#groups-and-households) section in the Features guide.
### `updateAt` is now `updatedAt`

View File

@@ -16,7 +16,7 @@ Recipes extras are a key feature of the Mealie API. They allow you to create cus
For example you could add `{"message": "Remember to thaw the chicken"}` to a recipe and use the webhooks built into mealie to send that message payload to a destination to be processed.
#### Shopping List and Food Extras
Similarly to recipes, extras are supported on shopping lists, shopping list items, and foods. At this time they are only accessible through the API. Extras for these objects allow for rich integrations between the Mealie shopping list and your favorite list manager, such as Alexa, ToDoist, Trello, or any other list manager with an API.
Similarly to recipes, extras are supported on shopping lists, shopping list items, and foods. At this time they are only accessible through the API. Extras for these objects allow for rich integrations between the Mealie shopping list and your favorite list manager, such as Todoist, Trello, or any other list manager with an API.
To keep shopping lists in sync, for instance, you can store your Trello list id on your Mealie shopping list: <br />
`{"trello_list_id": "5abbe4b7ddc1b351ef961414"}`
@@ -52,6 +52,7 @@ Many applications will keep track of the query and adjust the page parameter app
Notice that the route does not contain the baseurl (e.g. `https://mymealieapplication.com/api`).
There are a few shorthands available to reduce the number of calls for certain common requests:
- if you want to return _all_ results, effectively disabling pagination, set `perPage = -1` (and fetch the first page)
- if you want to fetch the _last_ page, set `page = -1`
@@ -89,6 +90,28 @@ This filter will find all recipes that don't start with the word "Test": <br>
This filter will find all recipes that have particular slugs: <br>
`slug IN ["pasta-fagioli", "delicious-ramen"]`
##### Placeholder Keywords
You can use placeholders to insert dynamic values as opposed to static values. Currently the only supported placeholder keyword is `$NOW`, to insert the current date/time.
`$NOW` can optionally be paired with basic offsets. Here is an example of a filter which gives you recipes not made within the past 30 days: <br>
`lastMade <= "$NOW-30d"`
Supported offset operations include:
- `-` for subtracting a time (i.e. in the past)
- `+` for adding a time (i.e. in the future)
Supported offset intervals include:
- `y` for years
- `m` for months
- `d` for days
- `H` for hours
- `M` for minutes
- `S` for seconds
Note that intervals are _case sensitive_ (e.g. `s` is an invalid interval).
##### Nested Property filters
When querying tables with relationships, you can filter properties on related tables. For instance, if you want to query all recipes owned by a particular user: <br>
`user.username = "SousChef20220320"`
@@ -96,7 +119,7 @@ When querying tables with relationships, you can filter properties on related ta
This timeline event filter will return all timeline events for recipes that were created after a particular date: <br>
`recipe.createdAt >= "2023-02-25"`
This recipe filter will return all recipes that contains a particular set of tags: <br>
This recipe filter will return all recipes that contain a particular set of tags: <br>
`tags.name CONTAINS ALL ["Easy", "Cajun"]`
##### Compound Filters

View File

@@ -122,17 +122,18 @@ For usage, see [Usage - OpenID Connect](../authentication/oidc-v2.md)
Mealie supports various integrations using OpenAI. For more information, check out our [OpenAI documentation](./open-ai.md).
For custom mapping variables (e.g. OPENAI_CUSTOM_HEADERS) you should pass values as JSON encoded strings (e.g. `OPENAI_CUSTOM_PARAMS='{"k1": "v1", "k2": "v2"}'`)
| Variables | Default | Description |
| ------------------------------------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OPENAI_BASE_URL<super>[&dagger;][secrets]</super> | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform |
| OPENAI_API_KEY<super>[&dagger;][secrets]</super> | None | Your OpenAI API Key. Enables OpenAI-related features |
| OPENAI_MODEL | gpt-4o | Which OpenAI model to use. If you're not sure, leave this empty |
| OPENAI_CUSTOM_HEADERS | None | Custom HTTP headers to add to all OpenAI requests. This should generally be left empty unless your custom service requires them |
| OPENAI_CUSTOM_PARAMS | None | Custom HTTP query params to add to all OpenAI requests. This should generally be left empty unless your custom service requires them |
| OPENAI_ENABLE_IMAGE_SERVICES | True | Whether to enable OpenAI image services, such as creating recipes via image. Leave this enabled unless your custom model doesn't support it, or you want to reduce costs |
| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs |
| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs |
| OPENAI_REQUEST_TIMEOUT | 300 | The number of seconds to wait for an OpenAI request to complete before cancelling the request. Leave this empty unless you're running into timeout issues on slower hardware |
| Variables | Default | Description |
|---------------------------------------------------|:-------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| OPENAI_BASE_URL<super>[&dagger;][secrets]</super> | None | The base URL for the OpenAI API. If you're not sure, leave this empty to use the standard OpenAI platform |
| OPENAI_API_KEY<super>[&dagger;][secrets]</super> | None | Your OpenAI API Key. Enables OpenAI-related features |
| OPENAI_MODEL | gpt-4o | Which OpenAI model to use. If you're not sure, leave this empty |
| OPENAI_CUSTOM_HEADERS | None | Custom HTTP headers to add to all OpenAI requests. This should generally be left empty unless your custom service requires them |
| OPENAI_CUSTOM_PARAMS | None | Custom HTTP query params to add to all OpenAI requests. This should generally be left empty unless your custom service requires them |
| OPENAI_ENABLE_IMAGE_SERVICES | True | Whether to enable OpenAI image services, such as creating recipes via image. Leave this enabled unless your custom model doesn't support it, or you want to reduce costs |
| OPENAI_WORKERS | 2 | Number of OpenAI workers per request. Higher values may increase processing speed, but will incur additional API costs |
| OPENAI_SEND_DATABASE_DATA | True | Whether to send Mealie data to OpenAI to improve request accuracy. This will incur additional API costs |
| OPENAI_REQUEST_TIMEOUT | 300 | The number of seconds to wait for an OpenAI request to complete before cancelling the request. Leave this empty unless you're running into timeout issues on slower hardware |
| OPENAI_CUSTOM_PROMPT_DIR | None | Path to custom prompt files. Only existing files in your custom directory will override the defaults; any missing or empty custom files will automatically fall back to the system defaults. See https://github.com/mealie-recipes/mealie/tree/mealie-next/mealie/services/openai/prompts for expected file names. |
### Theming

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.9.2`
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.10.2`
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.9.2 # (3)
image: ghcr.io/mealie-recipes/mealie:v3.10.2 # (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.9.2 # (3)
image: ghcr.io/mealie-recipes/mealie:v3.10.2 # (3)
container_name: mealie
restart: always
ports:

View File

File diff suppressed because one or more lines are too long

View File

@@ -15,7 +15,6 @@
:nudge-top="menuTop ? '5' : '0'"
allow-overflow
close-delay="125"
:open-on-hover="mdAndUp"
content-class="d-print-none"
>
<template #activator="{ props: activatorProps }">
@@ -83,8 +82,6 @@ const emit = defineEmits<{
[key: string]: [];
}>();
const { mdAndUp } = useDisplay();
const i18n = useI18n();
const { $globals } = useNuxtApp();
const api = useUserApi();
@@ -94,7 +91,7 @@ const state = reactive({
shoppingListDialog: false,
menuItems: [
{
title: i18n.t("recipe.add-to-list"),
title: i18n.t("meal-plan.add-day-to-list"),
icon: $globals.icons.cartCheck,
color: undefined,
event: "shoppingList",
@@ -126,8 +123,8 @@ async function getShoppingLists() {
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
shoppingList: () => {
getShoppingLists();
shoppingList: async () => {
await getShoppingLists();
state.shoppingListDialog = true;
},
};

View File

@@ -36,7 +36,7 @@
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
import { Organizer } from "~/lib/api/types/non-generated";
import type { QueryFilterJSON } from "~/lib/api/types/response";
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
interface Props {
queryFilter?: QueryFilterJSON | null;

View File

@@ -319,7 +319,7 @@ import { useDebounceFn } from "@vueuse/core";
import { useHouseholdSelf } from "~/composables/use-households";
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
import { Organizer } from "~/lib/api/types/non-generated";
import type { LogicalOperator, QueryFilterJSON, QueryFilterJSONPart, RelationalKeyword, RelationalOperator } from "~/lib/api/types/response";
import type { LogicalOperator, QueryFilterJSON, QueryFilterJSONPart, RelationalKeyword, RelationalOperator } from "~/lib/api/types/non-generated";
import { useCategoryStore, useFoodStore, useHouseholdStore, useTagStore, useToolStore } from "~/composables/store";
import { useUserStore } from "~/composables/store/use-user-store";
import { type Field, type FieldDefinition, type FieldValue, type OrganizerBase, useQueryFilterBuilder } from "~/composables/use-query-filter-builder";

View File

@@ -79,7 +79,7 @@
@print="$emit('print')"
/>
</div>
<div v-if="open" class="custom-btn-group gapped">
<div v-if="open" class="custom-btn-group gapped ma-1">
<v-btn
v-for="(btn, index) in editorButtons"
:key="index"

View File

@@ -1,60 +1,97 @@
<template>
<div v-if="model.length > 0 || edit">
<v-card class="mt-4">
<v-card-title class="py-2">
{{ $t("asset.assets") }}
</v-card-title>
<v-list-item class="pr-2 pl-0">
<v-card-title>
{{ $t("asset.assets") }}
</v-card-title>
<template #append>
<v-btn
v-if="edit"
variant="plain"
:icon="$globals.icons.create"
@click="state.newAssetDialog = true"
/>
</template>
</v-list-item>
<v-divider class="mx-2" />
<v-list
v-if="model.length > 0"
lines="two"
:flat="!edit"
>
<v-list-item
v-for="(item, i) in model"
:key="i"
:href="!edit ? assetURL(item.fileName ?? '') : ''"
target="_blank"
class="pr-2"
>
<template #prepend>
<div class="ma-auto">
<v-tooltip location="bottom">
<template #activator="{ props: tooltipProps }">
<v-icon v-bind="tooltipProps">
{{ getIconDefinition(item.icon).icon }}
</v-icon>
</template>
<span>{{ getIconDefinition(item.icon).title }}</span>
</v-tooltip>
</div>
<v-avatar size="48" rounded="lg" class="elevation-1">
<v-img
v-if="isImage(item.fileName)"
:src="assetURL(item.fileName ?? '')"
:alt="item.name"
loading="lazy"
cover
/>
<v-icon v-else size="large">
{{ getIconDefinition(item.icon).icon }}
</v-icon>
</v-avatar>
</template>
<v-list-item-title class="pl-2">
<v-list-item-title>
{{ item.name }}
</v-list-item-title>
<template #append>
<v-menu v-if="edit" location="bottom end">
<template #activator="{ props: menuProps }">
<v-btn
v-bind="menuProps"
icon
variant="plain"
>
<v-icon :icon="$globals.icons.dotsVertical" />
</v-btn>
</template>
<v-list density="compact" min-width="220">
<v-list-item
:href="assetURL(item.fileName ?? '')"
:prepend-icon="$globals.icons.eye"
:title="$t('general.view')"
target="_blank"
/>
<v-list-item
:href="assetURL(item.fileName ?? '')"
:prepend-icon="$globals.icons.download"
:title="$t('general.download')"
download
/>
<v-list-item
v-if="edit"
:prepend-icon="$globals.icons.contentCopy"
:title="$t('general.copy')"
@click="copyText(assetEmbed(item.fileName ?? ''))"
/>
<v-list-item
v-if="edit"
:prepend-icon="$globals.icons.delete"
:title="$t('general.delete')"
@click="model.splice(i, 1)"
/>
</v-list>
</v-menu>
<v-btn
v-if="!edit"
color="primary"
icon
size="small"
variant="plain"
:href="assetURL(item.fileName ?? '')"
target="_blank"
top
download
>
<v-icon> {{ $globals.icons.download }} </v-icon>
</v-btn>
<div v-else>
<v-btn
color="error"
icon
size="small"
top
@click="model.splice(i, 1)"
>
<v-icon>{{ $globals.icons.delete }}</v-icon>
</v-btn>
<AppButtonCopy
color=""
:copy-text="assetEmbed(item.fileName ?? '')"
/>
</div>
</template>
</v-list-item>
</v-list>
@@ -68,18 +105,9 @@
can-submit
@submit="addAsset"
>
<template #activator>
<BaseButton
v-if="edit"
size="small"
create
@click="state.newAssetDialog = true"
/>
</template>
<v-card-text class="pt-4">
<v-text-field
v-model="state.newAsset.name"
density="compact"
:label="$t('general.name')"
/>
<div class="d-flex justify-space-between">
@@ -92,10 +120,14 @@
item-value="name"
class="mr-2"
>
<template #item="{ item, props: itemProps }">
<template #item="{ props: itemProps, item }">
<v-list-item v-bind="itemProps">
<template #prepend>
<v-icon>{{ item.raw.icon }}</v-icon>
<v-avatar>
<v-icon>
{{ item.raw.icon }}
</v-icon>
</v-avatar>
</template>
</v-list-item>
</template>
@@ -107,7 +139,6 @@
@uploaded="setFileObject"
/>
</div>
{{ state.fileObject.name }}
</v-card-text>
</BaseDialog>
</div>
@@ -118,6 +149,7 @@
import { useStaticRoutes, useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import type { RecipeAsset } from "~/lib/api/types/recipe";
import { useCopy } from "~/composables/use-copy";
const props = defineProps({
slug: {
@@ -149,6 +181,7 @@ const state = reactive({
const i18n = useI18n();
const { $globals } = useNuxtApp();
const { copyText } = useCopy();
const iconOptions = [
{
@@ -184,21 +217,31 @@ function getIconDefinition(icon: string) {
return iconOptions.find(item => item.name === icon) || iconOptions[0];
}
function isImage(fileName?: string | null) {
if (!fileName) return false;
return /\.(png|jpe?g|gif|webp|bmp|avif)$/i.test(fileName);
}
const { recipeAssetPath } = useStaticRoutes();
function assetURL(assetName: string) {
return recipeAssetPath(props.recipeId, assetName);
}
function assetEmbed(name: string) {
return `<img src="${serverBase}${assetURL(name)}" height="100%" width="100%"> </img>`;
return `<img src="${serverBase}${assetURL(name)}" height="100%" width="100%" />`;
}
function setFileObject(fileObject: File) {
state.fileObject = fileObject;
// If the user didn't provide a name, default to the file base name
if (!state.newAsset.name?.trim()) {
state.newAsset.name = fileObject.name.substring(0, fileObject.name.lastIndexOf("."));
}
}
function validFields() {
return state.newAsset.name.length > 0 && state.fileObject.name.length > 0;
// Only require a file; name will fall back to the file name if empty
return Boolean(state.fileObject?.name);
}
async function addAsset() {
@@ -207,8 +250,10 @@ async function addAsset() {
return;
}
const nameToUse = state.newAsset.name?.trim() || state.fileObject.name;
const { data } = await api.recipes.createAsset(props.slug, {
name: state.newAsset.name,
name: nameToUse,
icon: state.newAsset.icon,
file: state.fileObject,
extension: state.fileObject.name.split(".").pop() || "",

View File

@@ -10,7 +10,6 @@
:nudge-top="menuTop ? '5' : '0'"
allow-overflow
close-delay="125"
:open-on-hover="$vuetify.display.mdAndUp"
content-class="d-print-none"
@update:model-value="onMenuToggle"
>
@@ -24,7 +23,6 @@
:fab="fab"
v-bind="activatorProps"
@click.prevent
@mouseenter="onHover"
>
<v-icon
:size="!fab ? undefined : 'x-large'"
@@ -127,12 +125,6 @@ const contentProps = computed(() => {
return rest;
});
function onHover() {
if (!isMenuContentLoaded.value) {
isMenuContentLoaded.value = true;
}
}
function onMenuToggle(isOpen: boolean) {
if (isOpen && !isMenuContentLoaded.value) {
isMenuContentLoaded.value = true;

View File

@@ -176,6 +176,7 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits<{
[key: string]: any;
deleted: [slug: string];
print: [];
}>();
const api = useUserApi();

View File

@@ -227,7 +227,7 @@ const currentHouseholdSlug = ref("");
const filteredShoppingLists = ref<ShoppingListSummary[]>([]);
const state = reactive({
shoppingListDialog: true,
shoppingListDialog: false,
shoppingListIngredientDialog: false,
shoppingListShowAllToggled: false,
});
@@ -237,8 +237,8 @@ const { shoppingListDialog, shoppingListIngredientDialog, shoppingListShowAllTog
const recipeIngredientSections = ref<ShoppingListRecipeIngredientSection[]>([]);
const selectedShoppingList = ref<ShoppingListSummary | null>(null);
watch(dialog, (newVal, oldVal) => {
if (newVal && !oldVal) {
watch([dialog, () => preferences.value.viewAllLists], () => {
if (dialog.value) {
currentHouseholdSlug.value = $auth.user.value?.householdSlug || "";
filteredShoppingLists.value = props.shoppingLists.filter(
list => preferences.value.viewAllLists || list.userId === $auth.user.value?.id,
@@ -249,10 +249,11 @@ watch(dialog, (newVal, oldVal) => {
openShoppingListIngredientDialog(selectedShoppingList.value);
}
else {
state.shoppingListDialog = true;
ready.value = true;
}
}
else if (!newVal) {
else if (!dialog.value) {
initState();
}
});
@@ -371,7 +372,7 @@ async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) {
}
function initState() {
state.shoppingListDialog = true;
state.shoppingListDialog = false;
state.shoppingListIngredientDialog = false;
state.shoppingListShowAllToggled = false;
recipeIngredientSections.value = [];

View File

@@ -17,15 +17,13 @@
v-for="(ingredient, index) in value"
:key="'ingredient' + index"
>
<template v-if="!isCookMode">
<h3
v-if="showTitleEditor[index]"
class="mt-2"
>
{{ ingredient.title }}
</h3>
<v-divider v-if="showTitleEditor[index]" />
</template>
<h3
v-if="showTitleEditor[index]"
class="mt-2"
>
{{ ingredient.title }}
</h3>
<v-divider v-if="showTitleEditor[index]" />
<v-list-item
density="compact"
class="pa-0"

View File

@@ -39,7 +39,6 @@
:nudge-top="props.menuTop ? '5' : '0'"
allow-overflow
close-delay="125"
:open-on-hover="!props.useMobileFormat"
content-class="d-print-none"
>
<template #activator="{ props: btnProps }">
@@ -99,7 +98,6 @@ const props = defineProps<{
color?: string;
event: RecipeTimelineEventOut;
menuIcon?: string | null;
useMobileFormat?: boolean;
}>();
const emit = defineEmits(["delete", "update"]);

View File

@@ -35,7 +35,6 @@
:menu-top="false"
:event="event"
:menu-icon="$globals.icons.dotsVertical"
:use-mobile-format="useMobileFormat"
color="transparent"
:elevation="0"
:card-menu="false"

View File

@@ -38,7 +38,7 @@
clearable
/>
<div />
<div class="d-flex py-4 px-1 align-center">
<div class="d-flex flex-wrap py-4 px-1 align-center">
<v-btn-toggle
v-if="requireAll != undefined"
v-model="combinator"
@@ -46,6 +46,7 @@
density="compact"
variant="outlined"
color="primary"
class="my-1"
>
<v-btn value="hasAll">
{{ $t('search.has-all') }}
@@ -58,6 +59,7 @@
<v-btn
size="small"
color="accent"
class="my-1"
@click="clearSelection"
>
{{ $t("search.clear-selection") }}

View File

@@ -56,25 +56,6 @@
width="250"
/>
</div>
<v-menu
v-if="listItem.recipeReferences && listItem.recipeReferences.length > 0"
open-on-hover
offset-y
start
top
>
<template #activator="{ props }">
<v-icon class="mt-auto" :icon="$globals.icons.alert" v-bind="props" color="warning">
{{ $globals.icons.alert }}
</v-icon>
</template>
<v-card max-width="350px" class="left-warning-border">
<v-card-text>
{{ $t("shopping-list.linked-item-warning") }}
</v-card-text>
</v-card>
</v-menu>
</div>
<BaseButton
v-if="listItem.labelId && listItem.food && listItem.labelId !== listItem.food.labelId"

View File

@@ -8,7 +8,6 @@
:nudge-top="menuTop ? '5' : '0'"
allow-overflow
close-delay="125"
open-on-hover
content-class="d-print-none"
>
<template #activator="{ props }">

View File

@@ -7,6 +7,10 @@
import DOMPurify from "isomorphic-dompurify";
import { marked } from "marked";
enum DOMPurifyHook {
UponSanitizeAttribute = "uponSanitizeAttribute",
}
export default defineNuxtComponent({
props: {
source: {
@@ -15,14 +19,26 @@ export default defineNuxtComponent({
},
},
setup(props) {
const ALLOWED_STYLE_TAGS = [
"background-color", "color", "font-style", "font-weight", "text-decoration", "text-align",
];
function sanitizeMarkdown(rawHtml: string | null | undefined): string {
if (!rawHtml) {
return "";
}
DOMPurify.addHook(DOMPurifyHook.UponSanitizeAttribute, (node, data) => {
if (data.attrName === "style") {
const styles = data.attrValue.split(";").filter((style) => {
const [property] = style.split(":");
return ALLOWED_STYLE_TAGS.includes(property.trim().toLowerCase());
});
data.attrValue = styles.join(";");
}
});
const sanitized = DOMPurify.sanitize(rawHtml, {
// List based on
// https://support.zendesk.com/hc/en-us/articles/4408824584602-Allowing-unsafe-HTML-in-help-center-articles
ALLOWED_TAGS: [
"strong", "em", "b", "i", "u", "p", "code", "pre", "samp", "kbd", "var", "sub", "sup", "dfn", "cite",
"small", "address", "hr", "br", "id", "div", "span", "h1", "h2", "h3", "h4", "h5", "h6",
@@ -31,10 +47,14 @@ export default defineNuxtComponent({
],
ALLOWED_ATTR: [
"href", "src", "alt", "height", "width", "class", "allow", "title", "allowfullscreen", "frameborder",
"scrolling", "cite", "datetime", "name", "abbr", "target", "border", "start",
"scrolling", "cite", "datetime", "name", "abbr", "target", "border", "start", "style",
],
});
Object.values(DOMPurifyHook).forEach((hook) => {
DOMPurify.removeHook(hook);
});
return sanitized;
}

View File

@@ -21,19 +21,19 @@ export const LOCALES = [
{
name: "Українська (Ukrainian)",
value: "uk-UA",
progress: 100,
progress: 83,
dir: "ltr",
},
{
name: "Türkçe (Turkish)",
value: "tr-TR",
progress: 41,
progress: 40,
dir: "ltr",
},
{
name: "Svenska (Swedish)",
value: "sv-SE",
progress: 68,
progress: 61,
dir: "ltr",
},
{
@@ -45,55 +45,55 @@ export const LOCALES = [
{
name: "Slovenščina (Slovenian)",
value: "sl-SI",
progress: 41,
progress: 40,
dir: "ltr",
},
{
name: "Slovenčina (Slovak)",
value: "sk-SK",
progress: 46,
progress: 47,
dir: "ltr",
},
{
name: "Pусский (Russian)",
value: "ru-RU",
progress: 46,
progress: 44,
dir: "ltr",
},
{
name: "Română (Romanian)",
value: "ro-RO",
progress: 45,
progress: 44,
dir: "ltr",
},
{
name: "Português (Portuguese)",
value: "pt-PT",
progress: 40,
progress: 39,
dir: "ltr",
},
{
name: "Português do Brasil (Brazilian Portuguese)",
value: "pt-BR",
progress: 49,
progress: 46,
dir: "ltr",
},
{
name: "Polski (Polish)",
value: "pl-PL",
progress: 53,
progress: 49,
dir: "ltr",
},
{
name: "Norsk (Norwegian)",
value: "no-NO",
progress: 43,
progress: 42,
dir: "ltr",
},
{
name: "Nederlands (Dutch)",
value: "nl-NL",
progress: 59,
progress: 54,
dir: "ltr",
},
{
@@ -105,13 +105,13 @@ export const LOCALES = [
{
name: "Lietuvių (Lithuanian)",
value: "lt-LT",
progress: 31,
progress: 30,
dir: "ltr",
},
{
name: "한국어 (Korean)",
value: "ko-KR",
progress: 39,
progress: 38,
dir: "ltr",
},
{
@@ -123,73 +123,73 @@ export const LOCALES = [
{
name: "Italiano (Italian)",
value: "it-IT",
progress: 52,
progress: 49,
dir: "ltr",
},
{
name: "Íslenska (Icelandic)",
value: "is-IS",
progress: 46,
progress: 43,
dir: "ltr",
},
{
name: "Magyar (Hungarian)",
value: "hu-HU",
progress: 48,
progress: 46,
dir: "ltr",
},
{
name: "Hrvatski (Croatian)",
value: "hr-HR",
progress: 29,
progress: 30,
dir: "ltr",
},
{
name: "עברית (Hebrew)",
value: "he-IL",
progress: 72,
progress: 64,
dir: "rtl",
},
{
name: "Galego (Galician)",
value: "gl-ES",
progress: 39,
progress: 38,
dir: "ltr",
},
{
name: "Français (French)",
value: "fr-FR",
progress: 74,
progress: 67,
dir: "ltr",
},
{
name: "Français canadien (Canadian French)",
value: "fr-CA",
progress: 99,
progress: 83,
dir: "ltr",
},
{
name: "Belge (Belgian)",
value: "fr-BE",
progress: 40,
progress: 39,
dir: "ltr",
},
{
name: "Suomi (Finnish)",
value: "fi-FI",
progress: 41,
progress: 40,
dir: "ltr",
},
{
name: "Eesti (Estonian)",
value: "et-EE",
progress: 47,
progress: 44,
dir: "ltr",
},
{
name: "Español (Spanish)",
value: "es-ES",
progress: 47,
progress: 45,
dir: "ltr",
},
{
@@ -201,43 +201,43 @@ export const LOCALES = [
{
name: "British English",
value: "en-GB",
progress: 45,
progress: 42,
dir: "ltr",
},
{
name: "Ελληνικά (Greek)",
value: "el-GR",
progress: 42,
progress: 41,
dir: "ltr",
},
{
name: "Deutsch (German)",
value: "de-DE",
progress: 98,
progress: 83,
dir: "ltr",
},
{
name: "Dansk (Danish)",
value: "da-DK",
progress: 66,
progress: 63,
dir: "ltr",
},
{
name: "Čeština (Czech)",
value: "cs-CZ",
progress: 45,
progress: 43,
dir: "ltr",
},
{
name: "Català (Catalan)",
value: "ca-ES",
progress: 41,
progress: 40,
dir: "ltr",
},
{
name: "Български (Bulgarian)",
value: "bg-BG",
progress: 51,
progress: 49,
dir: "ltr",
},
{

View File

@@ -2,6 +2,7 @@ import { useRecipeCreatePreferences } from "~/composables/use-users/preferences"
export interface UseNewRecipeOptionsProps {
enableImportKeywords?: boolean;
enableImportCategories?: boolean;
enableStayInEditMode?: boolean;
enableParseRecipe?: boolean;
}
@@ -9,6 +10,7 @@ export interface UseNewRecipeOptionsProps {
export function useNewRecipeOptions(props: UseNewRecipeOptionsProps = {}) {
const {
enableImportKeywords = true,
enableImportCategories = true,
enableStayInEditMode = true,
enableParseRecipe = true,
} = props;
@@ -27,6 +29,17 @@ export function useNewRecipeOptions(props: UseNewRecipeOptionsProps = {}) {
},
});
const importCategories = computed({
get() {
if (!enableImportCategories) return false;
return recipeCreatePreferences.value.importCategories;
},
set(v: boolean) {
if (!enableImportCategories) return;
recipeCreatePreferences.value.importCategories = v;
},
});
const stayInEditMode = computed({
get() {
if (!enableStayInEditMode) return false;
@@ -71,6 +84,7 @@ export function useNewRecipeOptions(props: UseNewRecipeOptionsProps = {}) {
return {
// Computed properties for the checkboxes
importKeywordsAsTags,
importCategories,
stayInEditMode,
parseRecipe,
@@ -79,6 +93,7 @@ export function useNewRecipeOptions(props: UseNewRecipeOptionsProps = {}) {
// Props for conditional rendering
enableImportKeywords,
enableImportCategories,
enableStayInEditMode,
enableParseRecipe,
};

View File

@@ -1,5 +1,5 @@
import { Organizer, type RecipeOrganizer } from "~/lib/api/types/non-generated";
import type { LogicalOperator, RelationalKeyword, RelationalOperator } from "~/lib/api/types/response";
import { Organizer } from "~/lib/api/types/non-generated";
import type { LogicalOperator, RecipeOrganizer, RelationalKeyword, RelationalOperator } from "~/lib/api/types/non-generated";
export interface FieldLogicalOperator {
label: string;

View File

@@ -1,7 +1,7 @@
import { useLocalStorage, useSessionStorage } from "@vueuse/core";
import { ActivityKey } from "~/lib/api/types/activity";
import type { RegisteredParser, TimelineEventType } from "~/lib/api/types/recipe";
import type { QueryFilterJSON } from "~/lib/api/types/response";
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
export interface UserPrintPreferences {
imagePosition: string;
@@ -63,6 +63,7 @@ export interface UserRecipeFinderPreferences {
export interface UserRecipeCreatePreferences {
importKeywordsAsTags: boolean;
importCategories: boolean;
stayInEditMode: boolean;
parseRecipe: boolean;
}
@@ -233,6 +234,7 @@ export function useRecipeCreatePreferences(): Ref<UserRecipeCreatePreferences> {
"recipe-create-preferences",
{
importKeywordsAsTags: false,
importCategories: false,
stayInEditMode: false,
parseRecipe: true,
},

View File

@@ -212,6 +212,8 @@
"upload-file": "Laai dokument op",
"created-on-date": "Geskep op: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Kon nie kopieer na die knipbord toe nie.",
"confirm-delete-generic-items": "Is jy seker jy wil die volgende items verwyder?",
"organizers": "Organiseerders",
@@ -367,7 +369,9 @@
"recipe-rules": "Resepreëls",
"applies-to-all-days": "Van toepassing op alle dae",
"applies-on-days": "Van toepassing op {0}s",
"meal-plan-settings": "Maaltydplan verstellings"
"meal-plan-settings": "Maaltydplan verstellings",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Migrasiedata is uitgevee",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Voer oorspronklike sleutelwoorde as merkers in",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Bly in redigeer modus",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Voer vanaf zip in",

View File

@@ -212,6 +212,8 @@
"upload-file": "تحميل الملف",
"created-on-date": "تم الإنشاء في {0}",
"unsaved-changes": "لديك تغييرات غير محفوظة. هل تريد الحفظ قبل المغادرة؟ حسنًا للحفظ، قم بإلغاء تجاهل التغييرات.",
"discard-changes": "إلغاء التغييرات",
"discard-changes-description": "لديك تغييرات غير محفوظة. هل أنت متأكد من أنك تريد تجاهلها؟",
"clipboard-copy-failure": "فشل في النسخ إلى الحافظة.",
"confirm-delete-generic-items": "هل أنت متأكد أنك تريد حذف المجموعات التالية؟",
"organizers": "المنظمون",
@@ -367,7 +369,9 @@
"recipe-rules": "قواعد الوصفات",
"applies-to-all-days": "ينطبق على جميع الأيام",
"applies-on-days": "يطبق على أيام {0}",
"meal-plan-settings": "إعدادات خِطَّة الوجبات الغذائية"
"meal-plan-settings": "إعدادات خِطَّة الوجبات الغذائية",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "حذف بيانات الهجرة",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "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",
"stay-in-edit-mode": "البقاء في وضع التعديل",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "استيراد من ملف Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Качване на файл",
"created-on-date": "Добавена на {0}",
"unsaved-changes": "Имате незапазени промени. Желаете ли да ги запазите преди да излезете? Натиснете Ок за запазване и Отказ за отхвърляне на промените.",
"discard-changes": "Отхвърляне на промените",
"discard-changes-description": "Имате незаписани промени, сигурни ли сте, че искате да ги отмените?",
"clipboard-copy-failure": "Линкът към рецептата е копиран в клипборда.",
"confirm-delete-generic-items": "Сигурни ли сте, че желаете да изтриете следните елементи?",
"organizers": "Органайзер",
@@ -367,7 +369,9 @@
"recipe-rules": "Правила на рецептата",
"applies-to-all-days": "Прилага се за всички дни",
"applies-on-days": "Всеки/всяка {0}",
"meal-plan-settings": "Настройки на плана за хранене"
"meal-plan-settings": "Настройки на плана за хранене",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Данните за мигриране са премахнати",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Блокиран ли е уебсайтът?",
"scrape-recipe-try-importing-raw-html-instead": "Опитайте вместо това да импортирате суровия HTML код.",
"import-original-keywords-as-tags": "Добави оригиналните ключови думи като етикети",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Остани в режим на редакция",
"parse-recipe-ingredients-after-import": "Анализиране на съставките на рецептата след импортиране",
"import-from-zip": "Импортирай от Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Puja un fitxer",
"created-on-date": "Creat el: {0}",
"unsaved-changes": "Tens canvis que no estan guardats. Vols guardar-los abans de sortir? Clica d'acord per guardar-los o cancel·lar per descartar els canvis.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "No s'ha pogut copiar al porta-retalls.",
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
"organizers": "Organitzadors",
@@ -367,7 +369,9 @@
"recipe-rules": "Normes per la recepta",
"applies-to-all-days": "Aplica a tots els dies",
"applies-on-days": "S'aplicarà en {0}s",
"meal-plan-settings": "Opcions de planificació de menús"
"meal-plan-settings": "Opcions de planificació de menús",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "S'han suprimit les dades migrades",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "S'està bloquejant el lloc web?",
"scrape-recipe-try-importing-raw-html-instead": "Prova important l'HTML directament.",
"import-original-keywords-as-tags": "Importa les paraules clau originals com a tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Segueix en el mode d'edició",
"parse-recipe-ingredients-after-import": "Analitza els ingredients de la recepta després d'importar",
"import-from-zip": "Importa des d'un ZIP",

View File

@@ -212,6 +212,8 @@
"upload-file": "Nahrát soubor",
"created-on-date": "Vytvořeno dne: {0}",
"unsaved-changes": "Máte neuložené změny. Chcete je uložit před odchodem? Klikněte Okay pro uložení, Cancel pro smazání změn.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Zkopírování do schránky se nezdařilo.",
"confirm-delete-generic-items": "Opravdu chcete smazat následující položky?",
"organizers": "Organizace",
@@ -367,7 +369,9 @@
"recipe-rules": "Pravidla receptu",
"applies-to-all-days": "Použije se na všechny dny",
"applies-on-days": "Platí pro {0}",
"meal-plan-settings": "Nastavení jídelníčku"
"meal-plan-settings": "Nastavení jídelníčku",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Data z migrace byla smazána",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Webové stránky jsou blokovány?",
"scrape-recipe-try-importing-raw-html-instead": "Zkuste namísto toho importovat raw HTML.",
"import-original-keywords-as-tags": "Importovat původní klíčová slova jako štítky",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Zůstat v režimu úprav",
"parse-recipe-ingredients-after-import": "Po importu analyzovat ingredience receptu",
"import-from-zip": "Importovat ze zipu",

View File

@@ -212,6 +212,8 @@
"upload-file": "Upload fil",
"created-on-date": "Oprettet den: {0}",
"unsaved-changes": "Du har ændringer som ikke er gemt. Vil du gemme før du forlader? Vælg \"Okay\" for at gemme, eller \"Annullér\" for at kassere ændringer.",
"discard-changes": "Kassér ændringer",
"discard-changes-description": "Du har ændringer, der ikke er gemt. Er du sikker på, at du vil kassere dem?",
"clipboard-copy-failure": "Kopiering til udklipsholderen mislykkedes.",
"confirm-delete-generic-items": "Er du sikker på at du ønsker at slette de valgte emner?",
"organizers": "Organisatorer",
@@ -367,7 +369,9 @@
"recipe-rules": "Opskriftsregler",
"applies-to-all-days": "Gælder for alle dage",
"applies-on-days": "Gælder for {0}e",
"meal-plan-settings": "Indstillinger for madplanlægning"
"meal-plan-settings": "Indstillinger for madplanlægning",
"add-all-to-list": "Tilføj alle til liste",
"add-day-to-list": "Tilføj dag til liste"
},
"migration": {
"migration-data-removed": "Migreringsdata fjernet",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Bliver hjemmesiden blokeret?",
"scrape-recipe-try-importing-raw-html-instead": "Forsøg at importere den rå HTML i stedet.",
"import-original-keywords-as-tags": "Importér originale nøgleord som mærker",
"import-original-categories": "Importér originale kategorier",
"stay-in-edit-mode": "Bliv i redigeringstilstand",
"parse-recipe-ingredients-after-import": "Fortolk opskrift ingredienser efter import",
"import-from-zip": "Importer fra zip-fil",

View File

@@ -212,6 +212,8 @@
"upload-file": "Datei hochladen",
"created-on-date": "Erstellt am: {0}",
"unsaved-changes": "Du hast ungespeicherte Änderungen. Möchtest du vor dem Verlassen speichern? OK um zu speichern, Cancel um Änderungen zu verwerfen.",
"discard-changes": "Änderungen verwerfen",
"discard-changes-description": "Du hast ungespeicherte Änderungen. Bist du sicher, dass du sie verwerfen möchtest?",
"clipboard-copy-failure": "Fehler beim Kopieren in die Zwischenablage.",
"confirm-delete-generic-items": "Bist du dir sicher, dass du die folgenden Einträge löschen möchtest?",
"organizers": "Organisieren",
@@ -367,7 +369,9 @@
"recipe-rules": "Rezeptregeln",
"applies-to-all-days": "Gilt an allen Tagen",
"applies-on-days": "Gilt {0}s",
"meal-plan-settings": "Essensplan Einstellungen"
"meal-plan-settings": "Essensplan Einstellungen",
"add-all-to-list": "Alle zur Einkaufsliste hinzufügen",
"add-day-to-list": "Tag zur Einkaufsliste hinzufügen"
},
"migration": {
"migration-data-removed": "Migrationsdaten entfernt",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Die Website wird blockiert?",
"scrape-recipe-try-importing-raw-html-instead": "Versuche stattdessen das reine HTML zu importieren.",
"import-original-keywords-as-tags": "Importiere ursprüngliche Stichwörter als Schlagwörter",
"import-original-categories": "Importiere ursprüngliche Kategorien",
"stay-in-edit-mode": "Im Bearbeitungsmodus bleiben",
"parse-recipe-ingredients-after-import": "Zutaten nach dem Import parsen",
"import-from-zip": "Von Zip importieren",

View File

@@ -212,6 +212,8 @@
"upload-file": "Μεταφόρτωση αρχείου",
"created-on-date": "Δημιουργήθηκε στις: {0}",
"unsaved-changes": "Εχετε μη αποθηκευμένες αλλαγές. Θέλετε να κάνετε αποθήκευση πριν από την αποχώρηση; Εντάξει για αποθήκευση, Ακυρο για απόρριψη των αλλαγών.",
"discard-changes": "Απόρριψη αλλαγών",
"discard-changes-description": "Εχετε μη αποθηκευμένες αλλαγές, θέλετε σίγουρα να τις απορρίψετε;",
"clipboard-copy-failure": "Η αντιγραφή στο πρόχειρο απέτυχε.",
"confirm-delete-generic-items": "Θέλετε σίγουρα να διαγράψετε τα ακόλουθα αντικείμενα;",
"organizers": "Οργανωτές",
@@ -367,7 +369,9 @@
"recipe-rules": "Κανόνες Συνταγής",
"applies-to-all-days": "Εφαρμόζεται για όλες τις ημέρες",
"applies-on-days": "Εφαρμόζεται κάθε {0}",
"meal-plan-settings": "Ρυθμίσεις προγράμματος γευμάτων"
"meal-plan-settings": "Ρυθμίσεις προγράμματος γευμάτων",
"add-all-to-list": "Προσθήκη όλων στη λίστα",
"add-day-to-list": "Προσθήκη ημέρας στη λίστα"
},
"migration": {
"migration-data-removed": "Τα δεδομένα μετεγκατάστασης καταργήθηκαν",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Η ιστοσελίδα μπλοκάρεται;",
"scrape-recipe-try-importing-raw-html-instead": "Δοκιμάστε να εισάγετε τον ακατέργαστο κώδικα HTML.",
"import-original-keywords-as-tags": "Εισαγωγή αρχικών λέξεων-κλειδιών ως ετικέτες",
"import-original-categories": "Εισαγωγή αρχικών κατηγοριών",
"stay-in-edit-mode": "Παραμονή σε λειτουργία επεξεργασίας",
"parse-recipe-ingredients-after-import": "Ανάλυση συστατικών συνταγής μετά την εισαγωγή",
"import-from-zip": "Εισαγωγή μέσω zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Failed to copy to the clipboard.",
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
"organizers": "Organisers",
@@ -367,7 +369,9 @@
"recipe-rules": "Recipe Rules",
"applies-to-all-days": "Applies to all days",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"meal-plan-settings": "Meal Plan Settings",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Migration data removed",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Import original keywords as tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Stay in Edit mode",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Import from Zip",

View File

@@ -369,7 +369,9 @@
"recipe-rules": "Recipe Rules",
"applies-to-all-days": "Applies to all days",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"meal-plan-settings": "Meal Plan Settings",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Migration data removed",
@@ -642,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Import original keywords as tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Stay in Edit mode",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Import from Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Subir Archivo",
"created-on-date": "Creado el {0}",
"unsaved-changes": "Tienes cambios sin guardar. ¿Quieres guardar antes de salir? Aceptar para guardar, Cancelar para descartar cambios.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "No se pudo copiar al portapapeles.",
"confirm-delete-generic-items": "¿Estás seguro que quieres eliminar los siguientes elementos?",
"organizers": "Organizadores",
@@ -367,7 +369,9 @@
"recipe-rules": "Reglas de Recetas",
"applies-to-all-days": "Aplica para todos los días",
"applies-on-days": "Se aplica en {0}s",
"meal-plan-settings": "Configuración del Plan de Comidas"
"meal-plan-settings": "Configuración del Plan de Comidas",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Datos de migración eliminados",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "¿Sitio web bloqueado?",
"scrape-recipe-try-importing-raw-html-instead": "Intenta importar el HTML en bruto.",
"import-original-keywords-as-tags": "Importar palabras clave originales como etiquetas",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Permanecer en modo edición",
"parse-recipe-ingredients-after-import": "Analizar los ingredientes de la receta después de importarla",
"import-from-zip": "Importar desde zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Lae fail üles",
"created-on-date": "Loodud: {0}",
"unsaved-changes": "Sul on salvestamata muudatusi. Kas sa tahad salvestada enne lehelt lahkumist? Vajuta OK salvestamiseks või Tühista, et muudatused tühistada.",
"discard-changes": "Loobu muudatustest",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Lõikepuhvrisse kopeerimine ebaõnnestus.",
"confirm-delete-generic-items": "Kas oled kindel, et tahad kustutada järgnevad asjad?",
"organizers": "Korraldajad",
@@ -342,9 +344,9 @@
"breakfast": "Hommikusöök",
"lunch": "Lõuna",
"dinner": "Õhtusöök",
"snack": "Snack",
"drink": "Drink",
"dessert": "Dessert",
"snack": "Snäkk",
"drink": "Jook",
"dessert": "Magustoit",
"type-any": "Kõik",
"day-any": "Kõik",
"editor": "Editor",
@@ -367,7 +369,9 @@
"recipe-rules": "Retsepti reeglid",
"applies-to-all-days": "Kehtib kõikide päevade kohta",
"applies-on-days": "Kehtib {0}l",
"meal-plan-settings": "Toitumisplaani sätted"
"meal-plan-settings": "Toitumisplaani sätted",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Ületoomiste andmed eemaldatud",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Impordi originaal võtmesõnad siltidena",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Püsige redigeerimisrežiimis",
"parse-recipe-ingredients-after-import": "Tuvasta retsepti koostisosad pärast importimist",
"import-from-zip": "Impordi .zip-st",

View File

@@ -212,6 +212,8 @@
"upload-file": "Tuo tiedosto",
"created-on-date": "Luotu {0}",
"unsaved-changes": "Et ole tallentanut tekemiäsi muutoksia. Tallennetaanko ne? Paina \"ok\" tallentaaksesi ja \"peruuta\", jos et halua tallentaa.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Kopioiminen leikepöydälle epäonnistui.",
"confirm-delete-generic-items": "Haluatko varmasti poistaa seuraavat kohteet?",
"organizers": "Järjestäjät",
@@ -367,7 +369,9 @@
"recipe-rules": "Reseptimääritykset",
"applies-to-all-days": "Sovelletaan kaikkiin päiviin",
"applies-on-days": "Käytetään {0}",
"meal-plan-settings": "Ateriasuunnitelman asetukset"
"meal-plan-settings": "Ateriasuunnitelman asetukset",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Tuodut tiedot poistettu",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Onko sivusto estetty?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Tuo alkuperäiset avainsanat tunnisteiksi",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Pysy muokkaustilassa",
"parse-recipe-ingredients-after-import": "Jäsennä reseptin ainesosat tuonnin jälkeen",
"import-from-zip": "Tuo zip-arkistosta",

View File

@@ -212,6 +212,8 @@
"upload-file": "Transférer un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Échec de la copie dans le presse-papiers.",
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
"organizers": "Classification",
@@ -367,7 +369,9 @@
"recipe-rules": "Règles de recette",
"applies-to-all-days": "S'applique à tous les jours",
"applies-on-days": "S'applique les {0}s",
"meal-plan-settings": "Paramètres des menus"
"meal-plan-settings": "Paramètres des menus",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Données de migration supprimées",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Le site web est bloqué ?",
"scrape-recipe-try-importing-raw-html-instead": "Essayez plutôt d'importer le code HTML brut.",
"import-original-keywords-as-tags": "Importer les mots-clés d'origine en tant que tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Rester en mode édition",
"parse-recipe-ingredients-after-import": "Analyser les ingrédients de la recette après l'import",
"import-from-zip": "Importer depuis un zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Téléverser un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous les enregistrer? Ok pour enregistrer, annuler pour ignorer les modifications.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Échec de la copie vers le presse-papiers.",
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
"organizers": "Classification",
@@ -367,7 +369,9 @@
"recipe-rules": "Règles de recette",
"applies-to-all-days": "S'applique à tous les jours",
"applies-on-days": "S'applique les {0}s",
"meal-plan-settings": "Paramètres des menus"
"meal-plan-settings": "Paramètres des menus",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Données de migration supprimées",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Le site web est bloqué ?",
"scrape-recipe-try-importing-raw-html-instead": "Essayez plutôt d'importer le code HTML brut.",
"import-original-keywords-as-tags": "Importer les mots-clés d'origine en tant que tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Rester en mode édition",
"parse-recipe-ingredients-after-import": "Analyser les ingrédients de la recette après l'import",
"import-from-zip": "Importer depuis un zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Téléverser un fichier",
"created-on-date": "Créé le {0}",
"unsaved-changes": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer avant de partir? OK pour enregistrer, Annuler pour ignorer les modifications.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Échec de la copie dans le presse-papiers.",
"confirm-delete-generic-items": "Êtes-vous sûr de vouloir supprimer les éléments suivants ?",
"organizers": "Classification",
@@ -367,7 +369,9 @@
"recipe-rules": "Règles de recette",
"applies-to-all-days": "S'applique à tous les jours",
"applies-on-days": "S'applique les {0}s",
"meal-plan-settings": "Paramètres des menus"
"meal-plan-settings": "Paramètres des menus",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Données de migration supprimées",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Le site web est bloqué ?",
"scrape-recipe-try-importing-raw-html-instead": "Essayez plutôt d'importer le code HTML brut.",
"import-original-keywords-as-tags": "Importer les mots-clés d'origine en tant que tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Rester en mode édition",
"parse-recipe-ingredients-after-import": "Analyser les ingrédients de la recette après l'import",
"import-from-zip": "Importer depuis un zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Subir Arquivo",
"created-on-date": "Creado o: {0}",
"unsaved-changes": "Tes cambios sen gardar. Queres gardar antes de saír? OK para gardar, Cancelar para descartar cambios.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Produciuse un erro ao copiar contido no portapapeis.",
"confirm-delete-generic-items": "Estás seguro de que queres eliminar os seguintes elementos?",
"organizers": "Organizadores",
@@ -367,7 +369,9 @@
"recipe-rules": "Regras da Receita",
"applies-to-all-days": "Aplícase a todos os días",
"applies-on-days": "Aplícase en {0}s",
"meal-plan-settings": "Axustes do Menú"
"meal-plan-settings": "Axustes do Menú",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Elimináronse os datos de migración",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Importar palavras-chave orixinais como etiquetas",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Permanecer no modo de edición",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Importar de Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "העלאת קבצים",
"created-on-date": "נוצר ב-{0}",
"unsaved-changes": "יש שינויים שלא נשמרו. לצאת לפני שמירה? אשר לשמירה, בטל למחיקת שינויים.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "כשלון בהעתקה ללוח ההדבקה.",
"confirm-delete-generic-items": "למחוק את הפריטים שנבחרו?",
"organizers": "מארגנים",
@@ -367,7 +369,9 @@
"recipe-rules": "חוקי מתכון",
"applies-to-all-days": "החל על כל הימים",
"applies-on-days": "חל על {0}",
"meal-plan-settings": "הגדרות תכנון ארוחות"
"meal-plan-settings": "הגדרות תכנון ארוחות",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "מידע ממוגרץ נמחק",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "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",
"stay-in-edit-mode": "השאר במצב עריכה",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "ייבא מקובץ",

View File

@@ -212,6 +212,8 @@
"upload-file": "Prenesi Datoteku",
"created-on-date": "Kreirano dana: {0}",
"unsaved-changes": "Imate promjene koje nisu spremljene. Želite li ih spremiti prije odlaska? Ok za spremanje, Odustani za odbaciti promjene.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Pogreška prilikom spremanja u međuspremnik.",
"confirm-delete-generic-items": "Jeste li sigurni da želite izbrisati ove stavke?",
"organizers": "Organizatori",
@@ -367,7 +369,9 @@
"recipe-rules": "Pravila Recepata",
"applies-to-all-days": "Primjeni na sve dane",
"applies-on-days": "Primjeni na {0}",
"meal-plan-settings": "Postavke Plana Obroka"
"meal-plan-settings": "Postavke Plana Obroka",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Podaci o migraciji su uklonjeni",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Stranica ne radi?",
"scrape-recipe-try-importing-raw-html-instead": "Pokušajte s uvozom HTML-a.",
"import-original-keywords-as-tags": "Uvezi originalne ključne riječi kao oznake",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Ostanite u načinu uređivanja",
"parse-recipe-ingredients-after-import": "Parsiranje sastojaka recepta nakon uvoza",
"import-from-zip": "Uvoz iz Zip-a",

View File

@@ -212,6 +212,8 @@
"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-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?",
"organizers": "Rendszerezők",
@@ -367,7 +369,9 @@
"recipe-rules": "Recept szabályok",
"applies-to-all-days": "Minden napra vonatkozóan",
"applies-on-days": "Érvényes {0}-ként",
"meal-plan-settings": "Menütervező beállításai"
"meal-plan-settings": "Menütervező beállításai",
"add-all-to-list": "Összes hozzáadása a listához",
"add-day-to-list": "Nap hozzáadása a listához"
},
"migration": {
"migration-data-removed": "Migrációs adatok eltávolítva",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "A weboldal blokkolva van?",
"scrape-recipe-try-importing-raw-html-instead": "Próbálja meg inkább a nyers HTML-t importálni.",
"import-original-keywords-as-tags": "Eredeti kulcsszavak importálása címkeként",
"import-original-categories": "Eredeti kategóriák importálása",
"stay-in-edit-mode": "Maradjon Szerkesztés módban",
"parse-recipe-ingredients-after-import": "Recept összetevőinek elemzése importálás után",
"import-from-zip": "Importálás ZIP-ből",

View File

@@ -212,6 +212,8 @@
"upload-file": "Hlaða upp skrá",
"created-on-date": "Búið til: {0}",
"unsaved-changes": "Þú hefur ekki vistað breytingar. Viltu vista áður en þú ferð? Ýttu á \"Í lagi\" til að vista, \"Hætta við\" til að henda breytingum.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Mistókst að afrita klippispjaldið.",
"confirm-delete-generic-items": "Ertu viss um að þú viljir eyða eftirfylgjandi atriðum?",
"organizers": "Skipuleggjarar",
@@ -367,7 +369,9 @@
"recipe-rules": "Uppskriftar reglur",
"applies-to-all-days": "Á við alla daga",
"applies-on-days": "Gildir þegar er {0},",
"meal-plan-settings": "Stillingar matarplans"
"meal-plan-settings": "Stillingar matarplans",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Gagnaflutningur fjarlægður",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Er vefsíðan lokuð?",
"scrape-recipe-try-importing-raw-html-instead": "Reyndu að flytja inn HTML kóðann í staðinn.",
"import-original-keywords-as-tags": "Nota upprunanleg merki",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Vera í breytingarham",
"parse-recipe-ingredients-after-import": "Greina innhald uppskriftar eftir að búið er að hlaða inn uppskrift",
"import-from-zip": "Hlaða inn frá .zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Carica file",
"created-on-date": "Creato il: {0}",
"unsaved-changes": "Sono state apportate modifiche non salvate. Salvare prima di uscire? Premi Ok per salvare, Annulla per scartare le modifiche.",
"discard-changes": "Scarta le modifiche",
"discard-changes-description": "Sono state apportate modifiche non salvate. Sei sicuro di volerle scartare?",
"clipboard-copy-failure": "Impossibile copiare negli appunti.",
"confirm-delete-generic-items": "Sei sicuro di voler eliminare i seguenti elementi?",
"organizers": "Organizzatori",
@@ -367,7 +369,9 @@
"recipe-rules": "Regole per le ricette",
"applies-to-all-days": "Si applica a ogni giorno",
"applies-on-days": "Si applica ai {0}",
"meal-plan-settings": "Impostazioni del piano alimentare"
"meal-plan-settings": "Impostazioni del piano alimentare",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Dati di migrazione rimossi",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Il sito viene bloccato?",
"scrape-recipe-try-importing-raw-html-instead": "Prova a importare l'HTML puro.",
"import-original-keywords-as-tags": "Importa parole chiave originali come tag",
"import-original-categories": "Importa categorie originali",
"stay-in-edit-mode": "Rimani in modalità Modifica",
"parse-recipe-ingredients-after-import": "Analizza gli ingredienti della ricetta dopo l'importazione",
"import-from-zip": "Importa da Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "ファイルのアップロード",
"created-on-date": "作成日: {0}",
"unsaved-changes": "保存されていない変更があります。移動する前に保存しますか?保存するには はい を、変更を破棄するにはキャンセルしてください。",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "クリップボードにコピーできませんでした",
"confirm-delete-generic-items": "次のアイテムを本当に削除しますか?",
"organizers": "収納",
@@ -367,7 +369,9 @@
"recipe-rules": "レシピのルール",
"applies-to-all-days": "すべての日に適用",
"applies-on-days": "{0}曜日に適用",
"meal-plan-settings": "献立設定"
"meal-plan-settings": "献立設定",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "移行データが削除されました",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "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",
"stay-in-edit-mode": "編集モードを維持",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Zipからインポート",

View File

@@ -43,8 +43,8 @@
"category-deleted": "카테고리 삭제됨",
"category-deletion-failed": "카테고리 삭제 실패",
"category-filter": "카테고리 필터",
"category-update-failed": "Category 업데이트 실패",
"category-updated": "카테고리 업데이트",
"category-update-failed": "카테고리 수정 실패",
"category-updated": "카테고리 수정됨",
"uncategorized-count": "카테고리 없음 {count}",
"create-a-category": "카테고리 생성",
"category-name": "카테고리 이름",
@@ -56,7 +56,7 @@
"delete-event": "이벤트 삭제",
"event-delete-confirmation": "정말로 이 이벤트를 삭제하시겠어요?",
"event-deleted": "이벤트 삭제됨",
"event-updated": "이벤트 업데이트됨",
"event-updated": "이벤트 수정됨",
"new-notification-form-description": "Mealie는 Apprise 라이브러리를 사용하여 알림을 생성합니다. 알림에 사용할 서비스에 대한 다양한 옵션을 제공합니다. 서비스의 URL을 만드는 방법에 대한 종합적인 가이드는 해당 Wiki 문서를 참조하세요. 알림 유형에 따라 추가 기능이 포함될 수 있습니다.",
"new-version": "새로운 버전 사용 가능",
"notification": "알림",
@@ -69,12 +69,12 @@
"new-notification": "새 알림",
"event-notifiers": "이벤트 알림이",
"apprise-url-skipped-if-blank": "Apprise URL (비워두면 생략합니다)",
"apprise-url-is-left-intentionally-blank": "Apprise URL에는 일반적으로 민감한 정보가 포함되므로, 편집 시 이 필드는 의도적으로 비워둡니다. URL을 업데이트하려면 여기에 새 주소를 입력하시고, 현재 URL을 유지하려면 비워두십시오.",
"apprise-url-is-left-intentionally-blank": "Apprise URL에는 일반적으로 민감한 정보가 포함되므로, 편집 시 이 필드는 의도적으로 비워둡니다. URL을 수정하려면 여기에 새 주소를 입력하시고, 현재 URL을 유지하려면 비워두십시오.",
"enable-notifier": "알림 활성화",
"what-events": "이 알리미는 어떤 이벤트를 구독해야 합니까?",
"user-events": "사용자 이벤트",
"mealplan-events": "Mealplan 이벤트",
"when-a-user-in-your-group-creates-a-new-mealplan": "그룹의 사용자가 새로운 식 계획을 만들 때",
"when-a-user-in-your-group-creates-a-new-mealplan": "그룹의 사용자가 새로운 식 계획을 만들 때",
"shopping-list-events": "장보기 목록 이벤트",
"cookbook-events": "요리책 이벤트",
"tag-events": "Tag 이벤트",
@@ -92,7 +92,7 @@
"confirm-how-does-everything-look": "모든 게 어떻게 보이나요?",
"confirm-delete-generic": "이 항목을 삭제하시겠습니까?",
"copied_message": "복사됨!",
"create": "만들기",
"create": "생성",
"created": "생성됨",
"custom": "사용자 정의",
"dashboard": "대시보드",
@@ -212,9 +212,11 @@
"upload-file": "파일 업로드",
"created-on-date": "생성일: {0}",
"unsaved-changes": "저장되지 않은 변경 사항이 있습니다. 떠나기 전에 저장하시겠습니까? 저장하려면 확인을 클릭하고, 변경 사항을 삭제하려면 취소를 클릭합니다.",
"discard-changes": "변경사항 취소",
"discard-changes-description": "저장되지 않은 변경사항이 있습니다. 삭제하시겠습니까?",
"clipboard-copy-failure": "클립보드에 복사하는 데 실패했습니다.",
"confirm-delete-generic-items": "이 항목을 삭제하시겠습니까?",
"organizers": "분류자",
"organizers": "정리 도구",
"caution": "주의",
"show-advanced": "고급 표시",
"add-field": "필드 추가",
@@ -226,8 +228,8 @@
"cannot-delete-default-group": "기본 그룹은 삭제할 수 없습니다",
"cannot-delete-group-with-users": "사용자가 있는 그룹은 삭제할 수 없습니다.",
"confirm-group-deletion": "그룹을 삭제할까요?",
"create-group": "그룹 만들기",
"error-updating-group": "그룹 업데이트 오류",
"create-group": "그룹 생성",
"error-updating-group": "그룹 수정 오류",
"group": "그룹",
"group-deleted": "그룹 삭제됨",
"group-deletion-failed": "그룹 삭제 실패",
@@ -250,7 +252,7 @@
"manage": "관리",
"manage-household": "가구 관리",
"invite": "초대하기",
"looking-to-update-your-profile": "프로필을 업데이트하시겠습니까?",
"looking-to-update-your-profile": "프로필을 업데이트하고 싶으신가요?",
"default-recipe-preferences-description": "이 설정은 그룹에서 새 레시피를 생성할 때 적용되는 기본값입니다. 레시피 설정 메뉴에서 개별 레시피별로 변경할 수 있습니다.",
"default-recipe-preferences": "기본 레시피 설정",
"group-preferences": "그룹 설정",
@@ -304,34 +306,34 @@
"household-preferences": "가구 설정"
},
"meal-plan": {
"create-a-new-meal-plan": "새로운 식 계획 생성",
"update-this-meal-plan": "이 식 계획 업데이트",
"create-a-new-meal-plan": "새로운 식 계획 생성",
"update-this-meal-plan": "이 식 계획 수정",
"dinner-this-week": "이번 주 저녁 식사",
"dinner-today": "오늘 저녁 식사",
"dinner-tonight": "오늘 밤 저녁 식사",
"edit-meal-plan": "식 계획 편집",
"edit-meal-plan": "식 계획 편집",
"end-date": "종료 날짜",
"group": "그룹(베타)",
"main": "메인",
"meal-planner": "식단 플래너",
"meal-plans": "식단 계획",
"mealplan-categories": "식 계획 카테고리",
"mealplan-created": "식 계획이 생성됨",
"mealplan-creation-failed": "식 계획 생성 실패",
"mealplan-deleted": "식 계획 삭제됨",
"mealplan-deletion-failed": "식계획 삭제 실패",
"mealplan-settings": "식 계획 설정",
"mealplan-update-failed": "식 계획 업데이트 실패",
"mealplan-updated": "식 계획이 업데이트됨",
"mealplan-categories": "식 계획 카테고리",
"mealplan-created": "식 계획이 생성됨",
"mealplan-creation-failed": "식 계획 생성 실패",
"mealplan-deleted": "식 계획 삭제됨",
"mealplan-deletion-failed": "식계획 삭제 실패",
"mealplan-settings": "식 계획 설정",
"mealplan-update-failed": "식 계획 수정 실패",
"mealplan-updated": "식 계획 수정됨",
"mealplan-households-description": "가구를 선택하지 않은 경우 모든 가구의 레시피를 추가할 수 있습니다.",
"any-category": "모든 카테고리",
"any-tag": "모든 태그",
"any-household": "모든 가구",
"no-meal-plan-defined-yet": "아직 식 계획이 정의되지 않았습니다.",
"no-meal-planned-for-today": "오늘은 식 계획이 없습니다",
"no-meal-plan-defined-yet": "아직 식 계획이 정의되지 않았습니다.",
"no-meal-planned-for-today": "오늘은 식 계획이 없습니다",
"numberOfDays-hint": "페이지 로드 일수",
"numberOfDays-label": "기본 일수",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "이 카테고리의 레시피만 식 계획에 사용됩니다.",
"only-recipes-with-these-categories-will-be-used-in-meal-plans": "이 카테고리의 레시피만 식 계획에 사용됩니다.",
"planner": "플래너",
"quick-week": "빠른 주",
"side": "사이드",
@@ -362,12 +364,14 @@
"for-type-meal-types": "{0} 식사 종류에",
"meal-plan-rules": "식단 계획 규칙",
"new-rule": "새 규칙",
"meal-plan-rules-description": "식단 계획에 사용할 레시피를 자동 선택하는 규칙을 생성할 수 있습니다. 이 규칙들은 서버가 식사 플랜을 생성할 때 선택할 무작위 레시피 풀을 결정하는 데 사용됩니다. 동일한 요일/유형 제약 조건을 가진 규칙들은 필터가 병합된다는 점에 유의하세요. 실제로 중복 규칙을 생성할 필요는 없지만, 생성하는 것은 가능합니다.",
"meal-plan-rules-description": "식단 계획에 사용할 레시피를 자동 선택하는 규칙을 생성할 수 있습니다. 이 규칙들은 서버가 식단 계획을 생성할 때 선택할 무작위 레시피 풀을 결정하는 데 사용됩니다. 동일한 요일/유형 제약 조건을 가진 규칙들은 필터가 병합된다는 점에 유의하세요. 실제로 중복 규칙을 생성할 필요는 없지만, 생성하는 것은 가능합니다.",
"new-rule-description": "식단 계획에 새 규칙을 생성할 때, 특정 요일 및/또는 특정 식사 유형에만 적용되도록 규칙을 제한할 수 있습니다. 모든 요일 또는 모든 식사 유형에 규칙을 적용하려면 규칙을 \"모든\"으로 설정하면 됩니다. 이렇게 하면 해당 요일 및/또는 식사 유형의 모든 가능한 값에 규칙이 적용됩니다.",
"recipe-rules": "레시피 규칙",
"applies-to-all-days": "모든 날짜에 적용됨",
"applies-on-days": "{0}에 적용됨",
"meal-plan-settings": "식단 계획 설정"
"meal-plan-settings": "식단 계획 설정",
"add-all-to-list": "목록에 전체 추가",
"add-day-to-list": "목록에 날짜 추가"
},
"migration": {
"migration-data-removed": "이전된 데이터 제거됨",
@@ -451,7 +455,7 @@
"trim-prefix-description": "각 줄의 첫 문자 제거하기",
"split-by-numbered-line-description": "'1)' 또는 '1.' 패턴을 일치시켜 문단을 분할하려고 시도합니다.",
"import-by-url": "URL로 레시피 가져오기",
"create-manually": "수동으로 레시피 만들기",
"create-manually": "수동으로 레시피 생성",
"make-recipe-image": "이것을 레시피 이미지로 만드세요.",
"add-food": "식품 추가",
"add-recipe": "레시피 추가"
@@ -464,10 +468,10 @@
"page-creation-failed": "페이지 생성 실패",
"page-deleted": "페이지 삭제됨",
"page-deletion-failed": "페이지 삭제 실패",
"page-update-failed": "페이지 업데이트 실패",
"page-updated": "페이지 업데이트됨",
"pages-update-failed": "페이지 업데이트 실패",
"pages-updated": "페이지 업데이트됨",
"page-update-failed": "페이지 수정 실패",
"page-updated": "페이지 수정됨",
"pages-update-failed": "페이지 수정 실패",
"pages-updated": "페이지 수정됨",
"404-not-found": "404 찾을 수 없음",
"an-error-occurred": "오류가 발생했습니다!"
},
@@ -520,7 +524,7 @@
"recipe-creation-failed": "레시피 생성 실패",
"recipe-deleted": "레시피 삭제됨",
"recipe-image": "레시피 사진",
"recipe-image-updated": "레시피 사진 업데이트됨",
"recipe-image-updated": "레시피 사진 수정됨",
"delete-image": "레시피 사진 삭제",
"delete-image-confirmation": "이 레시피 이미지를 삭제하시겠습니까?",
"recipe-image-deleted": "레시피 이미지 삭제됨",
@@ -548,7 +552,7 @@
"no-recipe": "레시피 없음",
"locked-by-owner": "소유자에 의해 잠김",
"join-the-conversation": "대화에 참여하기",
"add-recipe-to-mealplan": "식 계획에 레시피 추가",
"add-recipe-to-mealplan": "식 계획에 레시피 추가",
"entry-type": "항목 유형",
"date-format-hint": "MM/DD/YYYY 형식",
"date-format-hint-yyyy-mm-dd": "YYYY-MM-DD 형식",
@@ -620,8 +624,8 @@
"create-recipe-description": "처음부터 새로운 레시피를 만드세요.",
"create-recipes": "레시피 생성",
"import-with-zip": ".zip 파일로 가져오기",
"create-recipe-from-an-image": "Create Recipe from an Image",
"create-recipe-from-an-image-description": "Create a recipe by uploading an image of it. Mealie will attempt to extract the text from the image using AI and create a recipe from it.",
"create-recipe-from-an-image": "이미지에서 레시피생성",
"create-recipe-from-an-image-description": "레시피 텍스트 이미지를 업로드하여 레시피를 생성하세요. Mealie는 AI를 사용하여 이미지에서 텍스트를 추출하고 이를 통해 새로운 레시피를 생성하려고 시도합니다.",
"crop-and-rotate-the-image": "이미지를 잘라내고 회전시켜 텍스트만 보이도록 하고 올바른 방향으로 배치하십시오.",
"create-from-images": "이미지에서 생성",
"should-translate-description": "레시피를 내 언어로 번역하기",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "웹사이트가 차단되고 있나요?",
"scrape-recipe-try-importing-raw-html-instead": "대신 원본 HTML 가져오기를 시도해보세요.",
"import-original-keywords-as-tags": "원본 키워드를 태그로 가져오기",
"import-original-categories": "원래 카테고리 불러오기",
"stay-in-edit-mode": "편집 모드 유지",
"parse-recipe-ingredients-after-import": "가져오기 후 레시피 재료 추출",
"import-from-zip": "Zip 파일에서 가져오기",
@@ -738,7 +743,7 @@
"search-mealie": "Mealie 검색 (/를 눌러보세요)",
"search-placeholder": "검색...",
"tag-filter": "태그 필터",
"search-hint": "'/'를 누르세요",
"search-hint": "'/'를 눌러보세요",
"advanced": "고급",
"auto-search": "자동 검색",
"no-results": "검색 결과가 없습니다.",
@@ -754,7 +759,7 @@
"restore-success": "복원 성공!",
"restore-fail": "복원이 실패했습니다. 자세한 내용은 서버 로그를 확인하십시오.",
"backup-tag": "백업 태그",
"create-heading": "Create a Backup",
"create-heading": "백업 생성하기",
"delete-backup": "백업 삭제",
"error-creating-backup-see-log-file": "백업 생성 중 오류 발생. 로그 파일을 참조하십시오.",
"full-backup": "전체 백업",
@@ -798,7 +803,7 @@
"profile": "프로필",
"remove-existing-entries-matching-imported-entries": "가져온 항목과 일치하는 기존 항목을 제거합니다",
"set-new-time": "새 시간 설정",
"settings-update-failed": "설정 업데이트 실패",
"settings-update-failed": "설정 수정 실패",
"settings-updated": "설정 업데이트",
"site-settings": "사이트 설정",
"theme": {
@@ -1024,7 +1029,7 @@
"register": "등록",
"reset-password": "비밀번호 재설정",
"sign-in": "로그인",
"total-mealplans": "전체 MealPlan 수",
"total-mealplans": "전체 식단 계획 수",
"total-users": "전체 사용자 수",
"upload-photo": "사진 업로드",
"use-8-characters-or-more-for-your-password": "비밀번호는 8자 이상으로 설정하십시오",
@@ -1068,7 +1073,7 @@
"user-details": "사용자 정보",
"user-name": "사용자 이름",
"authentication-method": "인증 방식",
"authentication-method-hint": "This specifies how a user will authenticate with Mealie. If you're not sure, choose 'Mealie",
"authentication-method-hint": "이 설정은 사용자가 Mealie에 어떻게 인증할지 지정합니다. 잘 모르겠다면 'Mealie'를 선택하세요.",
"permissions": "권한",
"administrator": "관리자",
"user-can-invite-other-to-group": "사용자는 다른 사용자를 그룹에 초대할 수 있습니다",
@@ -1095,8 +1100,8 @@
"foods": {
"merge-dialog-text": "선택한 식품을 병합하면 원본 식품과 대상 식품이 하나의 식품으로 합쳐집니다. 원본 식품은 삭제되며, 원본 식품에 대한 모든 참조는 대상 식품을 가리키도록 업데이트됩니다.",
"merge-food-example": "{food1}을 {food2}에 병합",
"seed-dialog-text": "Seed the database with foods based on your local language. This will create 200+ common foods that can be used to organize your database. Foods are translated via a community effort.",
"seed-dialog-warning": "You have already have some items in your database. This action will not reconcile duplicates, you will have to manage them manually.",
"seed-dialog-text": "당신이 사용하는 언어에 맞춰 데이터베이스에 음식 정보를 추가하세요. 이렇게 하면 데이터베이스를 구성하는 데 사용할 수 있는 약 2700가지의 일반적인 음식 정보가 생성됩니다. 음식 정보는 커뮤니티 참여를 통해 번역됩니다.",
"seed-dialog-warning": "데이터베이스에 이미 일부 항목이 있습니다. 이름이 같은 항목이 이미 존재하는 경우 새 항목은 추가되지 않습니다.",
"combine-food": "식품 병합",
"source-food": "원본 식품",
"target-food": "대상 식품",
@@ -1366,9 +1371,9 @@
"cookbooks-description": "레시피 카테고리 모음을 관리하고 해당 카테고리별 페이지를 생성합니다.",
"members": "회원",
"members-description": "가구 구성원을 확인하고 권한을 관리하세요.",
"webhooks-description": "Setup webhooks that trigger on days that you have have mealplan scheduled.",
"webhooks-description": "식단 계획이 예정된 날짜에 발동되는 웹훅을 설정하세요.",
"notifiers": "알리미",
"notifiers-description": "Setup email and push notifications that trigger on specific events.",
"notifiers-description": "특정 이벤트 발생 시 이메일 및 푸시 알림이 전송되도록 설정하세요.",
"manage-data": "데이터 관리하기",
"manage-data-description": "Mealie 데이터를 관리하세요; 식품, 단위, 카테고리, 태그 등.",
"data-migrations": "데이터 이전",

View File

@@ -212,6 +212,8 @@
"upload-file": "Įkelti failą",
"created-on-date": "Sukurta: {0}",
"unsaved-changes": "Turite neišsaugotų pakeitimų. Ar norite juos išsaugoti prieš išeidami?\nSpauskite „Gerai“, jei norite išsaugoti, arba „Atšaukti“, jei norite atmesti pakeitimus.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Nepavyko nukopijuoti į iškarpinę.",
"confirm-delete-generic-items": "Ar tikrai norite ištrinti šiuos elementus?",
"organizers": "Organizatoriai",
@@ -367,7 +369,9 @@
"recipe-rules": "Recepto taisyklės",
"applies-to-all-days": "Galioja visoms dienoms",
"applies-on-days": "Galioja {0}",
"meal-plan-settings": "Valgių plano nustatymai"
"meal-plan-settings": "Valgių plano nustatymai",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Perkėlimo duomenys pašalinti",
@@ -445,7 +449,7 @@
"upload-a-recipe": "Įkelti receptą",
"upload-individual-zip-file": "Įkelkite .zip failą, eksportuotą iš kitos \"Mealie\" sistemos.",
"url-form-hint": "Nukopijuokite ir įklijuokite nuorodą iš mėgstamų receptų svetainės",
"copy-and-paste-the-source-url-of-your-data-optional": "Copy and paste the source URL of your data (optional)",
"copy-and-paste-the-source-url-of-your-data-optional": "Nukopijuokite ir įklijuokite savo duomenų šaltinio URL (nebūtina)",
"view-scraped-data": "Peržiūrėti nuskaitytus duomenis",
"trim-whitespace-description": "Pašalinti tarpus bei tuščias eilutes pradžioje ir pabaigoje",
"trim-prefix-description": "Pašalinti kiekvienos eilutės pirmąjį ženklą",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Svetainė blokuojama?",
"scrape-recipe-try-importing-raw-html-instead": "Pabandykite vietoj to importuoti neapdorotą HTML.",
"import-original-keywords-as-tags": "Įkelti pradinius raktažodžius kaip žymas",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Toliau redaguoti",
"parse-recipe-ingredients-after-import": "Analizuoti recepto ingredientus po importavimo",
"import-from-zip": "Įkelti iš .ZIP archyvo",

View File

@@ -212,6 +212,8 @@
"upload-file": "Augšupielādēt failu",
"created-on-date": "Izveidots: {0}",
"unsaved-changes": "Jums ir nesaglabātas izmaiņas. Vai vēlaties ietaupīt pirms aiziešanas? Labi, lai saglabātu, Atcelt, lai izmestu izmaiņas.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Neizdevās kopēt starpliktuvē.",
"confirm-delete-generic-items": "Vai tiešām vēlaties dzēst šādus vienumus?",
"organizers": "Organizatori",
@@ -367,7 +369,9 @@
"recipe-rules": "Recepšu noteikumi",
"applies-to-all-days": "Attiecas uz visām dienām",
"applies-on-days": "Attiecas uz {0} s",
"meal-plan-settings": "Maltītes plāna iestatījumi"
"meal-plan-settings": "Maltītes plāna iestatījumi",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Migrācijas dati noņemti",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Importējiet oriģinālos atslēgvārdus kā tagus",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Palieciet rediģēšanas režīmā",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Importēt no Zip",

View File

@@ -4,7 +4,7 @@
"about-mealie": "Over Mealie",
"api-docs": "API-documentatie",
"api-port": "API-poort",
"application-mode": "Toepassingsmodus",
"application-mode": "Applicatiemodus",
"database-type": "Databasetype",
"database-url": "Database URL",
"default-group": "Standaardgroep",
@@ -212,6 +212,8 @@
"upload-file": "Bestand uploaden",
"created-on-date": "Gemaakt op: {0}",
"unsaved-changes": "Er zijn niet-opgeslagen wijzigingen. Wil je eerst opslaan voordat je vertrekt? Oké om op te slaan, Annuleren om wijzigingen ongedaan te maken.",
"discard-changes": "Sla wijzigingen niet op",
"discard-changes-description": "Je wilt je wijzigingen niet opslaan. Weet je het zeker?",
"clipboard-copy-failure": "Kopiëren naar klembord mislukt.",
"confirm-delete-generic-items": "Weet je zeker dat je de volgende items wilt verwijderen?",
"organizers": "Organisatoren",
@@ -367,7 +369,9 @@
"recipe-rules": "Receptregels",
"applies-to-all-days": "Van toepassing op alle dagen",
"applies-on-days": "Van toepassing op {0}s",
"meal-plan-settings": "Maaltijdplan-instellingen"
"meal-plan-settings": "Maaltijdplan-instellingen",
"add-all-to-list": "Alles aan lijst toevoegen",
"add-day-to-list": "Dag aan lijst toevoegen"
},
"migration": {
"migration-data-removed": "Migratiegegevens verwijderd",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Wordt de website geblokkeerd?",
"scrape-recipe-try-importing-raw-html-instead": "Probeer de HTML broncode te importeren.",
"import-original-keywords-as-tags": "Importeer oorspronkelijke trefwoorden als labels",
"import-original-categories": "Importeer oorspronkelijke categorieën",
"stay-in-edit-mode": "Blijf in bewerkingsmodus",
"parse-recipe-ingredients-after-import": "Ontleed de ingrediënten van het recept na importeren",
"import-from-zip": "Importeren uit zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Last opp fil",
"created-on-date": "Opprettet: {0}",
"unsaved-changes": "Du har ulagrede endringer. Ønsker du å lagre før du forlater? Trykk 'OK' for å lagre, 'Avbryt' for å forkaste endringene.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Kunne ikke kopiere til utklippstavlen.",
"confirm-delete-generic-items": "Er du sikker på at du vil slette følgende elementer?",
"organizers": "Organisatorer",
@@ -367,7 +369,9 @@
"recipe-rules": "Regler for oppskrifter",
"applies-to-all-days": "Gjelder for alle dager",
"applies-on-days": "Gjelder på {0}er",
"meal-plan-settings": "Innstillinger for måltidsplan"
"meal-plan-settings": "Innstillinger for måltidsplan",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Overføringsdata er fjernet",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Er nettstedet blokkert?",
"scrape-recipe-try-importing-raw-html-instead": "Prøv å importere HTML-koden i stedet.",
"import-original-keywords-as-tags": "Importer originale søkeord som emneord",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Forbli i redigeringsmodus",
"parse-recipe-ingredients-after-import": "Analyser oppskriftens ingredienser etter at importen er fullført",
"import-from-zip": "Importer fra zip-fil",

View File

@@ -212,6 +212,8 @@
"upload-file": "Prześlij plik",
"created-on-date": "Utworzono dnia: {0}",
"unsaved-changes": "Masz niezapisane zmiany. Czy chcesz zapisać przed wyjściem? Ok, aby zapisać, Anuluj, żeby odrzucić zmiany.",
"discard-changes": "Odrzuć zmiany",
"discard-changes-description": "Masz niezapisane zmiany. Czy na pewno chcesz je odrzucić?",
"clipboard-copy-failure": "Nie udało się skopiować do schowka.",
"confirm-delete-generic-items": "Czy na pewno chcesz usunąć następujące elementy?",
"organizers": "Organizatory",
@@ -367,7 +369,9 @@
"recipe-rules": "Reguły przepisów",
"applies-to-all-days": "Dotyczy wszystkich dni",
"applies-on-days": "Dotyczy {0}",
"meal-plan-settings": "Ustawienia planera posiłków"
"meal-plan-settings": "Ustawienia planera posiłków",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Dane migracji usunięte",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Witryna jest zablokowana?",
"scrape-recipe-try-importing-raw-html-instead": "Zamiast tego spróbuj zaimportować surowy HTML.",
"import-original-keywords-as-tags": "Importuj oryginalne słowa kluczowe jako tagi",
"import-original-categories": "Importuj oryginalne kategorie",
"stay-in-edit-mode": "Pozostań w trybie edycji",
"parse-recipe-ingredients-after-import": "Analizuj składniki receptury po zaimportowaniu",
"import-from-zip": "Importuj z pliku Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Enviar arquivo",
"created-on-date": "Criado em {0}",
"unsaved-changes": "Você possui alterações não salvas. Deseja salvar antes de sair? Ok para salvar, Cancelar para descartar alterações.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Falha ao copiar para a área de transferência.",
"confirm-delete-generic-items": "Tem certeza que quer excluir os itens seguintes?",
"organizers": "Organizadores",
@@ -367,7 +369,9 @@
"recipe-rules": "Regras das receitas",
"applies-to-all-days": "Aplica-se diariamente",
"applies-on-days": "Aplica-se em:",
"meal-plan-settings": "Configurações de plano de refeições"
"meal-plan-settings": "Configurações de plano de refeições",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Dados de migração removidos",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Site sendo bloqueado?",
"scrape-recipe-try-importing-raw-html-instead": "Tente importar o HTML ao invés disso.",
"import-original-keywords-as-tags": "Importar palavras-chave originais como marcadores",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Permanecer no modo de edição",
"parse-recipe-ingredients-after-import": "Interpretar os ingredientes da receita após importar",
"import-from-zip": "Importar do .zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Carregar ficheiro",
"created-on-date": "Criado em: {0}",
"unsaved-changes": "Tem alterações por gravar. Quer gravar antes de sair? OK para gravar, Cancelar para descartar alterações.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Erro ao copiar para a área de transferência.",
"confirm-delete-generic-items": "Tem a certeza de que deseja eliminar os seguintes itens?",
"organizers": "Organizadores",
@@ -367,7 +369,9 @@
"recipe-rules": "Regras das receitas",
"applies-to-all-days": "Aplica-se a todos os dias",
"applies-on-days": "Aplica-se em {0}s",
"meal-plan-settings": "Definições do Plano de Refeições"
"meal-plan-settings": "Definições do Plano de Refeições",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Dados de migração removidos",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "O site está bloqueado?",
"scrape-recipe-try-importing-raw-html-instead": "Tente importar o HTML bruto em vez disso.",
"import-original-keywords-as-tags": "Importar palavras-chave originais como etiquetas",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Permanecer no modo de edição",
"parse-recipe-ingredients-after-import": "Analisar ingredientes da receita após a importação",
"import-from-zip": "Importar de Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Încărcă fișier",
"created-on-date": "Creat pe {0}",
"unsaved-changes": "Aveți modificări nesalvate. Doriți să salvați înainte de a închide aplicația? Apăsați \"OK\" pentru a salva sau \"Anulare\" pentru a renunța la modificări.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Copierea în clipboard a eșuat.",
"confirm-delete-generic-items": "Sunteți sigur că doriți să ștergeți următoarele?",
"organizers": "Organizatori",
@@ -367,7 +369,9 @@
"recipe-rules": "Reguli rețetă",
"applies-to-all-days": "Se aplică pentru toate zilele",
"applies-on-days": "Aplică pe {0}s",
"meal-plan-settings": "Setările Planului de Masă"
"meal-plan-settings": "Setările Planului de Masă",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Datele migrării au fost șterse",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Site-ul este blocat?",
"scrape-recipe-try-importing-raw-html-instead": "Încercați în schimb să importați HTML-ul brut.",
"import-original-keywords-as-tags": "Importă cuvintele cheie originale ca tag-uri",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Rămâi în modul Editare",
"parse-recipe-ingredients-after-import": "Analizează ingredientele rețetei după import",
"import-from-zip": "Importă din zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Загрузить файл",
"created-on-date": "Создано: {0}",
"unsaved-changes": "У вас есть несохраненные изменения. Вы хотите сохранить их перед выходом?",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Не удалось скопировать текст.",
"confirm-delete-generic-items": "Вы уверены, что хотите удалить следующие элементы?",
"organizers": "Органайзеры",
@@ -367,7 +369,9 @@
"recipe-rules": "Правила рецептов",
"applies-to-all-days": "Применяется к всем дням",
"applies-on-days": "Применяется по {0}",
"meal-plan-settings": "Настройки плана питания"
"meal-plan-settings": "Настройки плана питания",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Данные миграции удалены",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Сайт заблокирован?",
"scrape-recipe-try-importing-raw-html-instead": "Попробуйте импортировать необработанный HTML файл.",
"import-original-keywords-as-tags": "Импортировать исходные ключевые слова как теги",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Остаться в режиме редактирования",
"parse-recipe-ingredients-after-import": "Распознавание ингредиентов рецепта после импорта",
"import-from-zip": "Импорт из архива",

View File

@@ -212,6 +212,8 @@
"upload-file": "Nahrať súbor",
"created-on-date": "Vytvorené: {0}",
"unsaved-changes": "Posledne vykonané zmeny nie sú uložené. Želáte si ich uložiť alebo zrušiť?",
"discard-changes": "Zahodiť zmeny",
"discard-changes-description": "Máte neuložené zmeny, naozaj ich chcete zahodiť?",
"clipboard-copy-failure": "Skopírovanie do schránky zlyhalo.",
"confirm-delete-generic-items": "Ste si istý, že chcete odstrániť nasledujúce položky?",
"organizers": "Organizéry",
@@ -367,7 +369,9 @@
"recipe-rules": "Pravidlá receptov",
"applies-to-all-days": "Platí pre všetky dni",
"applies-on-days": "Platí v {0}",
"meal-plan-settings": "Nastavenia jedálnička"
"meal-plan-settings": "Nastavenia jedálnička",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Prenesené dáta odstránené",
@@ -403,7 +407,7 @@
"title": "Tandoor recepty"
},
"cookn": {
"description-long": "Mealie can import recipes from DVO Cook'n X3. Export a cookbook or menu in the \"Cook'n\" format, rename the export extension to .zip, then upload the .zip below.",
"description-long": "Mealie môže importovať recepty z DVO Cook'n X3. Exportujte kuchársku knihu alebo menu vo formáte „Cook'n“, premenujte exportnú príponu na .zip a potom nahrajte súbor .zip nižšie.",
"title": "DVO Cook'n X3"
},
"recipe-data-migrations": "Migrácie dát receptov",
@@ -445,7 +449,7 @@
"upload-a-recipe": "Nahrať recept",
"upload-individual-zip-file": "Nahrať súbor .zip exportovaný z inej Mealie inštalácie.",
"url-form-hint": "Okopírujte a zložte odkaz z vašej obľúbenej webstránky",
"copy-and-paste-the-source-url-of-your-data-optional": "Copy and paste the source URL of your data (optional)",
"copy-and-paste-the-source-url-of-your-data-optional": "Skopírujte a vložte zdrojovú URL adresu vašich údajov (voliteľné)",
"view-scraped-data": "Náhľad získaných údajov",
"trim-whitespace-description": "Vymazať medzery a prázdne riadky na začiatku a na konci",
"trim-prefix-description": "Vymazať prvé písmeno z každého riadku",
@@ -597,7 +601,7 @@
"made-this": "Toto som uvaril",
"how-did-it-turn-out": "Ako to dopadlo?",
"user-made-this": "{user} toto uvaril(a)",
"made-for-recipe": "Made for {recipe}",
"made-for-recipe": "Vytvorené pre {recipe}",
"added-to-timeline": "Pridané na časovú os",
"failed-to-add-to-timeline": "Pridanie na časovú os skončilo chybou",
"failed-to-update-recipe": "Recept sa nepodarilo aktualizovať",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Webstránka je blokovaná?",
"scrape-recipe-try-importing-raw-html-instead": "Skúste miesto toho naimportovať čisté HTML.",
"import-original-keywords-as-tags": "Importovať pôvodné kľúčové slová ako štítky",
"import-original-categories": "Importovať pôvodné kategórie",
"stay-in-edit-mode": "Zostať v režime editovania",
"parse-recipe-ingredients-after-import": "Analyzovať ingrediencie po importe",
"import-from-zip": "Importovať zo Zip-súboru",
@@ -704,7 +709,7 @@
"cover-image": "Titulný obrázok",
"include-linked-recipes": "Zahrnúť prepojené Recepty",
"include-linked-recipe-ingredients": "Zahrnúť Prísady prepojených receptov",
"toggle-recipe": "Toggle Recipe"
"toggle-recipe": "Prepnúť recept"
},
"recipe-finder": {
"recipe-finder": "Hľadač receptov",
@@ -1081,7 +1086,7 @@
"forgot-password": "Zabudnuté heslo",
"forgot-password-text": "Prosím zadajte svoju e-mailovú adresu a my vám zašleme odkaz, ktorý vám umožní heslo obnoviť.",
"changes-reflected-immediately": "Zmeny týkajúce sa tohto používateľa budú vykonané okamžite.",
"default-activity": "Default Activity",
"default-activity": "Predvolená aktivita",
"default-activity-hint": "Vyberte na ktorú štránku chcete byť nasmerovaný po prihlásení z tohto zariadenia"
},
"language-dialog": {

View File

@@ -212,6 +212,8 @@
"upload-file": "Naloži datoteko",
"created-on-date": "Ustvarjeno dne: {0}",
"unsaved-changes": "Imate neohranjene spremembe. Ali želite shraniti pred odhodom? V redu, če želite shraniti, Prekliči, če želite zavreči spremembe.",
"discard-changes": "Zavrzi spremembe",
"discard-changes-description": "Imate neshranjene spremembe. Ste prepričani, da jih želite zavreči?",
"clipboard-copy-failure": "Kopiranje na odložišče ni bilo uspešno.",
"confirm-delete-generic-items": "Ali ste prepričani, da želite izbrisati izbrane elemente?",
"organizers": "Organizatorji",
@@ -367,7 +369,9 @@
"recipe-rules": "Pravila za recepte",
"applies-to-all-days": "Velja za vse dneve",
"applies-on-days": "Velja za {0}",
"meal-plan-settings": "Nastavitve jedilnika"
"meal-plan-settings": "Nastavitve jedilnika",
"add-all-to-list": "Dodaj vse na seznam",
"add-day-to-list": "Dodaj dan na seznam"
},
"migration": {
"migration-data-removed": "Migrirani podatki so odstranjeni",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Je spletna stran blokirana?",
"scrape-recipe-try-importing-raw-html-instead": "Poskusite namesto tega uvoziti surovi HTML.",
"import-original-keywords-as-tags": "Uvozi izvorne ključne besede kot značke",
"import-original-categories": "Uvozi izvirne kategorije",
"stay-in-edit-mode": "Urejaj naprej",
"parse-recipe-ingredients-after-import": "Razčlenitev sestavin recepta po uvozu",
"import-from-zip": "Uvozi z Zip",

View File

@@ -1,14 +1,14 @@
{
"about": {
"about": "О апликацији",
"about-mealie": "О Мили",
"about-mealie": "О Mealie",
"api-docs": "API документација",
"api-port": "API прикључак",
"application-mode": "Режим апликације",
"database-type": "Тип базе података",
"database-url": "URL базе података",
"default-group": "Подразумевана група",
"default-household": "Default Household",
"default-household": "Подразумевано домаћинство",
"demo": "Демо",
"demo-status": "Демо статус",
"development": "Развој",
@@ -16,7 +16,7 @@
"download-log": "Преузми дневник евиденције",
"download-recipe-json": "Последњи прикупљени JSON",
"github": "GitHub",
"log-lines": "Log Lines",
"log-lines": "Лог",
"not-demo": "Није демо",
"portfolio": "Портфолио",
"production": "У продукцији",
@@ -65,11 +65,11 @@
"something-went-wrong": "Нешто је кренуло погрешно!",
"subscribed-events": "Догађаји на које сте претплаћени",
"test-message-sent": "Тест порука је послата",
"message-sent": "Message Sent",
"message-sent": "Порука послата",
"new-notification": "Ново обавештење",
"event-notifiers": "Обавештавач о догађају",
"apprise-url-skipped-if-blank": "Apprise URL (прескочено ако је празно)",
"apprise-url-is-left-intentionally-blank": "Since Apprise URLs typically contain sensitive information, this field is left intentionally blank while editing. If you wish to update the URL, please enter the new one here, otherwise leave it blank to keep the current URL.",
"apprise-url-is-left-intentionally-blank": "Како Apprise URL обично садржи сензитивне информације, ово поље је намерно остављено празно. Ако желите да промените URL, упишите нови овде или оставите поље празно да задржите тренутни URL.",
"enable-notifier": "Омогући обавештење",
"what-events": "На које догађаје би требао да се претплати овај обавештавач?",
"user-events": "Догађаји корисника",
@@ -84,12 +84,12 @@
"label-events": "Label Events"
},
"general": {
"add": "Add",
"add": "Додај",
"cancel": "Откажи",
"clear": "Обриши",
"close": "Затвори",
"confirm": "Потврди",
"confirm-how-does-everything-look": "How does everything look?",
"confirm-how-does-everything-look": "Како све изгледа?",
"confirm-delete-generic": "Да ли сте сигурни да желите обрисати ово?",
"copied_message": "Копирано!",
"create": "Креирај",
@@ -118,12 +118,12 @@
"image-upload-failed": "Неуспешно додавање слике",
"import": "Увоз",
"json": "JSON",
"keyword": "Ključna reč",
"keyword": "Кључна реч",
"link-copied": "Линк је копиран",
"loading": "Loading",
"loading": "Учитавање",
"loading-events": "Учитавање догађаја",
"loading-recipe": "Loading recipe...",
"loading-ocr-data": "Loading OCR data...",
"loading-recipe": "Рецепт се учитава...",
"loading-ocr-data": "Учитавање OCR података...",
"loading-recipes": "Учитавање рецепата",
"message": "Порука",
"monday": "Понедељак",
@@ -134,7 +134,7 @@
"no-recipe-found": "Рецепт није пронађен",
"ok": "У реду",
"options": "Опције:",
"plural-name": "Ime u množini",
"plural-name": "Име у множини",
"print": "Штампа",
"print-preferences": "Подешавање штампе",
"random": "Насумично",
@@ -148,23 +148,23 @@
"save": "Сачувај",
"settings": "Подешавања",
"share": "Подели",
"show-all": "Show All",
"show-all": "Прикажи све",
"shuffle": "Помешано",
"sort": "Сортирај",
"sort-ascending": "Sort Ascending",
"sort-descending": "Sort Descending",
"sort-ascending": "Сложи по реду - растуће",
"sort-descending": "Сложи по реду - опадајуће",
"sort-alphabetically": "Азбучно",
"status": "Статус",
"subject": "Наслов",
"submit": "Пошаљи",
"success-count": "Успешно {count}",
"sunday": "недеља",
"system": "System",
"system": "Систем",
"templates": "Шаблони:",
"test": "Тест",
"themes": "Теме",
"thursday": "четвртак",
"title": "Title",
"title": "Наслов",
"token": "Токен",
"tuesday": "уторак",
"type": "Тип",
@@ -179,12 +179,12 @@
"units": "Јединице",
"back": "Назад",
"next": "Сљедећи",
"start": "Start",
"start": "Старт",
"toggle-view": "Промени приказ",
"date": "Датум",
"id": д",
"id": Д",
"owner": "Власник",
"change-owner": "Change Owner",
"change-owner": "Промени власника",
"date-added": "Датум додавања",
"none": "Ниједно",
"run": "Покрени",
@@ -211,15 +211,17 @@
"refresh": "Освежи",
"upload-file": "Учитај датотеку",
"created-on-date": "Крерирано: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"clipboard-copy-failure": "Failed to copy to the clipboard.",
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
"organizers": "Organizers",
"caution": "Caution",
"show-advanced": "Show Advanced",
"add-field": "Add Field",
"date-created": "Date Created",
"date-updated": "Date Updated"
"unsaved-changes": "Имате несачуване измене. Да ли желите да их сачувате пре изласка? ОК за потврду, Откажи да откажете измене.",
"discard-changes": "Откажи измене",
"discard-changes-description": "Имате несачуване измене. Да ли желите да их откажете?",
"clipboard-copy-failure": "Копирање није успело.",
"confirm-delete-generic-items": "Да ли желите да обришете следеће ставке?",
"organizers": "Организатор",
"caution": "Пажња",
"show-advanced": "Прикажи напредно",
"add-field": "Додај поље",
"date-created": "Датум креирања",
"date-updated": "Датум измене"
},
"group": {
"are-you-sure-you-want-to-delete-the-group": "Да ли сте сигурни да желите да обришете <b>{groupName}<b/>?",
@@ -234,7 +236,7 @@
"group-id-with-value": "ID групе: {groupID}",
"group-name": "Назив групе",
"group-not-found": "Група није пронађена",
"group-token": "Group Token",
"group-token": "Групни токен",
"group-with-value": "Група: {groupID}",
"groups": "Групе",
"manage-groups": "Управљај групама",
@@ -246,18 +248,18 @@
"keep-my-recipes-private-description": "Поставља вашу групу и све рецепте као подразумевано приватне. Увек можете касније променити ово."
},
"manage-members": "Управљај члановима",
"manage-members-description": "Manage the permissions of the members in your household. {manage} allows the user to access the data-management page, and {invite} allows the user to generate invitation links for other users. Group owners cannot change their own permissions.",
"manage-members-description": "Управљање дозволама у сопственом домаћинству. {manage} дозвољава кориснику приступ менаџменту података, а {invite} омогућава кориснику да генерише позивнице за друге кориснике. Власници групе не могу променити сопствене дозволе.",
"manage": "Управљај",
"manage-household": "Manage Household",
"manage-household": "Менаџмент домаћинства",
"invite": "Позови",
"looking-to-update-your-profile": "Желите ли да ажурирате свој профил?",
"default-recipe-preferences-description": "Ово су подразумевана подешавања када се креира нови рецепт у вашој групи. Ова подешавања могу бити промењена за појединачне рецепте у менију подешавања рецепата.",
"default-recipe-preferences": "Подразумевана подешавања рецепта",
"group-preferences": "Подешавања групе",
"private-group": "Приватна група",
"private-group-description": "Setting your group to private will disable all public view options. This overrides any individual public view settings",
"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",
"private-group-description": "Подешавњем групе као приватне ће онемогућити јавни преглед у потпуности. Ово поништава све индивидуална подешавања јавног приступа",
"enable-public-access": "Дозволи јавни приступ",
"enable-public-access-description": "Подеси рецепте у групи уобичајно као јавне и дозволи гостима преглед рецепата без логовања",
"allow-users-outside-of-your-group-to-see-your-recipes": "Дозволите корисницима, који су ван ваше групе, да виде ваше рецепте",
"allow-users-outside-of-your-group-to-see-your-recipes-description": "Када је омогућено, можете користити јавну везу за дељење одређених рецепата без одобравања корисника. Када је онемогућено, рецепте можете делити само са корисницима који су у вашој групи или помоћу претходно генерисане приватне везе",
"show-nutrition-information": "Прикажи информације о исхрани",
@@ -269,37 +271,37 @@
"disable-users-from-commenting-on-recipes": "Онемогући кориснике да коментаришу рецепте",
"disable-users-from-commenting-on-recipes-description": "Сакрива секцију коментара на страници рецепта и онемогућава коментаре",
"disable-organizing-recipe-ingredients-by-units-and-food": "Онемогући организацију састојака рецепта по јединицама и намирницама",
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Hides the Food, Unit, and Amount fields for ingredients and treats ingredients as plain text fields",
"disable-organizing-recipe-ingredients-by-units-and-food-description": "Сакриј тип, јединицу мере и количину за састојке и третирај их као поље са обичним текстом",
"general-preferences": "Општа подешавања",
"group-recipe-preferences": "Подешавања групе рецепта",
"report": "Извештај",
"report-with-id": "Report ID: {id}",
"report-with-id": "ИД извештаја: {id}",
"group-management": "Управљање групом",
"admin-group-management": "Управљање администраторском групом",
"admin-group-management-text": "Промене у овој групи биће одмах видљиве.",
"group-id-value": "Group Id: {0}",
"total-households": "Total Households",
"you-must-select-a-group-before-selecting-a-household": "You must select a group before selecting a household"
"group-id-value": "ИД групе: {0}",
"total-households": "Укупно домаћинстава",
"you-must-select-a-group-before-selecting-a-household": "Морате селектовати групу пре селектовања домаћинства"
},
"household": {
"household": "Household",
"households": "Households",
"user-household": "User Household",
"create-household": "Create Household",
"household-name": "Household Name",
"household-group": "Household Group",
"household-management": "Household Management",
"manage-households": "Manage Households",
"admin-household-management": "Admin Household Management",
"admin-household-management-text": "Changes to this household will be reflected immediately.",
"household-id-value": "Household Id: {0}",
"private-household": "Private Household",
"private-household-description": "Setting your household to private will disable all public view options. This overrides any individual public view settings",
"lock-recipe-edits-from-other-households": "Lock recipe edits from other households",
"lock-recipe-edits-from-other-households-description": "When enabled only users in your household can edit recipes created by your household",
"household-recipe-preferences": "Household Recipe Preferences",
"default-recipe-preferences-description": "These are the default settings when a new recipe is created in your household. These can be changed for individual recipes in the recipe settings menu.",
"allow-users-outside-of-your-household-to-see-your-recipes": "Allow users outside of your household to see your recipes",
"household": "Домаћинство",
"households": "Домаћинства",
"user-household": "Корисниково домаћинство",
"create-household": "Креирај домаћинство",
"household-name": "Назив домаћинства",
"household-group": "Група домаћинства",
"household-management": "Управљање домаћинством",
"manage-households": "Управљање домаћинствима",
"admin-household-management": "Административно управљање домаћинством",
"admin-household-management-text": "Промене за ово домаћинство ће бити видљиве одмах.",
"household-id-value": "ИД домаћинства {0}",
"private-household": "Приватно домаћинство",
"private-household-description": "Подешавање сопственог домаћинства као приватно ће онемогућити сав јавни преглед. Ово поништава сва индивидуална подешавања за преглед",
"lock-recipe-edits-from-other-households": "Забрани измене рецепата од стране других домаћинстава",
"lock-recipe-edits-from-other-households-description": "Када је опција укључена, само корисници из вашег домаћинства могу мењати рецепте креиране унутар њега",
"household-recipe-preferences": "Подешавање рецепата за домаћинство",
"default-recipe-preferences-description": "Ово су уобичајна подешавања када се нови рецепт креира у вашем домаћинству. Подешавања могу бити промењена за појединачне рецепте из менија за подешавања.",
"allow-users-outside-of-your-household-to-see-your-recipes": "Дозволите корисницима ван вашег домаћинства да прегледају ваше рецепте",
"allow-users-outside-of-your-household-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 household or with a pre-generated private link",
"household-preferences": "Household Preferences"
},
@@ -367,7 +369,9 @@
"recipe-rules": "Правила за рецепт",
"applies-to-all-days": "Applies to all days",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"meal-plan-settings": "Meal Plan Settings",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Migration data removed",
@@ -503,7 +507,7 @@
"insert-below": "Insert Below",
"instructions": "Instructions",
"key-name-required": "Key Name Required",
"landscape-view-coming-soon": "Landscape View (Coming Soon)",
"landscape-view-coming-soon": "Хорижонтални поглед (Ускоро)",
"milligrams": "milligrams",
"new-key-name": "New Key Name",
"no-white-space-allowed": "No White Space Allowed",
@@ -620,7 +624,7 @@
"create-recipe-description": "Create a new recipe from scratch.",
"create-recipes": "Create Recipes",
"import-with-zip": "Увези помоћу .zip архиве",
"create-recipe-from-an-image": "Create Recipe from an Image",
"create-recipe-from-an-image": "Направи рецепт на основи слике",
"create-recipe-from-an-image-description": "Create a recipe by uploading an image of it. Mealie will attempt to extract the text from the image using AI and create a recipe from it.",
"crop-and-rotate-the-image": "Crop and rotate the image so that only the text is visible, and it's in the correct orientation.",
"create-from-images": "Create from Images",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "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",
"stay-in-edit-mode": "Stay in Edit mode",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Увези из Zip архиве",
@@ -754,7 +759,7 @@
"restore-success": "Restore successful",
"restore-fail": "Restore failed. Check your server logs for more details",
"backup-tag": "Backup Tag",
"create-heading": "Create a Backup",
"create-heading": "Креирај бекап",
"delete-backup": "Delete Backup",
"error-creating-backup-see-log-file": "Error Creating Backup. See Log File",
"full-backup": "Full Backup",
@@ -899,13 +904,13 @@
"create-shopping-list": "Направи списак за куповину",
"from-recipe": "From Recipe",
"list-name": "List Name",
"new-list": "Novi spisak",
"new-list": "Нови списак",
"quantity": "Quantity: {0}",
"shopping-list": "Shopping List",
"shopping-lists": "Списак за куповину",
"food": "Храна",
"note": "Note",
"label": "Natpis",
"label": "Натпис",
"save-label": "Save Label",
"linked-item-warning": "This item is linked to one or more recipe. Adjusting the units or foods will yield unexpected results when adding or removing the recipe from this list.",
"toggle-food": "Toggle Food",
@@ -1408,33 +1413,33 @@
},
"query-filter": {
"logical-operators": {
"and": "AND",
"or": "OR"
"and": "И",
"or": "ИЛИ"
},
"relational-operators": {
"equals": "equals",
"does-not-equal": "does not equal",
"is-greater-than": "is greater than",
"is-greater-than-or-equal-to": "is greater than or equal to",
"is-less-than": "is less than",
"is-less-than-or-equal-to": "is less than or equal to"
"equals": "једнако са",
"does-not-equal": "није једнако са",
"is-greater-than": "је веће од",
"is-greater-than-or-equal-to": "је веће или једнако са",
"is-less-than": "је мање од",
"is-less-than-or-equal-to": "је мање или једнако са"
},
"relational-keywords": {
"is": "is",
"is-not": "is not",
"is-one-of": "is one of",
"is-not-one-of": "is not one of",
"contains-all-of": "contains all of",
"is-like": "is like",
"is-not-like": "is not like"
"is": "је",
"is-not": "није ",
"is-one-of": "је једно од",
"is-not-one-of": "није једно од",
"contains-all-of": "садржи све из",
"is-like": "је као",
"is-not-like": "није као"
}
},
"validators": {
"required": "This Field is 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} Characters"
"required": "Ово поље је обавезно",
"invalid-email": "Имејл мора бити валидан",
"invalid-url": "Мора бити валидан URL",
"no-whitespace": "Размак није дозвољен",
"min-length": "Мора бити најмање {min} карактера",
"max-length": "Мора бити највише {max} карактера"
}
}

View File

@@ -212,6 +212,8 @@
"upload-file": "Ladda upp fil",
"created-on-date": "Skapad {0}",
"unsaved-changes": "Du har osparade ändringar. Vill du spara innan du lämnar? Tryck Okej att spara, Avbryt för att ignorera ändringar.",
"discard-changes": "Överge ändringar",
"discard-changes-description": "Du har osparade ändringar. Är du säker på att du vill överge dem?",
"clipboard-copy-failure": "Det gick inte att kopiera till urklipp.",
"confirm-delete-generic-items": "Är du säker på att du vill radera följande objekt?",
"organizers": "Organisatörer",
@@ -367,7 +369,9 @@
"recipe-rules": "Receptregler",
"applies-to-all-days": "Gäller för alla dagar",
"applies-on-days": "Gäller på {0}s",
"meal-plan-settings": "Inställningar för måltidsplanering"
"meal-plan-settings": "Inställningar för måltidsplanering",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Importerad data borttagen",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Är websidan blockerad?",
"scrape-recipe-try-importing-raw-html-instead": "Prova att importera HTML istället.",
"import-original-keywords-as-tags": "Importera ursprungliga sökord som taggar",
"import-original-categories": "Importera ursprungliga kategorier",
"stay-in-edit-mode": "Stanna kvar i redigeringsläge",
"parse-recipe-ingredients-after-import": "Tolka receptingredienser efter import",
"import-from-zip": "Importera från zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Dosya Yükle",
"created-on-date": "{0} tarihinde oluşturuldu",
"unsaved-changes": "Kaydedilmemiş değişiklikleriniz mevcut. Ayrılmadan önce kaydetmek ister misiniz? Kaydetmek için Tamam'ı, değişiklikleri iptal etmek için İptal'i seçin.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Panoya kopyalanamadı.",
"confirm-delete-generic-items": "Aşağıdaki öğeleri silmek istediğinizden emin misiniz?",
"organizers": "Organizatörler",
@@ -367,7 +369,9 @@
"recipe-rules": "Tarif Kuralları",
"applies-to-all-days": "Tüm günler için geçerlidir",
"applies-on-days": "{0} günlerine geçerlidir",
"meal-plan-settings": "Öğün Planı Ayarları"
"meal-plan-settings": "Öğün Planı Ayarları",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Taşıma verileri kaldırıldı",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Web sitesi engelleniyor?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Orijinal anahtar kelimeleri etiket olarak içe aktar",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Düzenleme modunda kalın",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Zip'ten içeri aktar",

View File

@@ -212,6 +212,8 @@
"upload-file": "Завантажити файл",
"created-on-date": "Створено: {0}",
"unsaved-changes": "У вас є незбережені зміни. Ви хочете зберегти їх перед виходом? Гаразд, щоб зберегти, Скасувати, щоб скасувати.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Не вдалося скопіювати до буфера обміну.",
"confirm-delete-generic-items": "Ви впевнені, що хочете видалити вибрані елементи?",
"organizers": "Органайзери",
@@ -367,7 +369,9 @@
"recipe-rules": "Правила рецептів",
"applies-to-all-days": "Застосовується до всіх днів",
"applies-on-days": "Застосовується на {0}сек",
"meal-plan-settings": "Налаштування меню"
"meal-plan-settings": "Налаштування меню",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Дані міграції видалені",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Заблокований сайт?",
"scrape-recipe-try-importing-raw-html-instead": "Спробуйте імпортувати вихідний HTML натомість.",
"import-original-keywords-as-tags": "Імпортувати оригінальні ключові слова як теги",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Залишитися в режимі редактора",
"parse-recipe-ingredients-after-import": "Розпізнавання інгредієнтів рецепту після імпорту",
"import-from-zip": "Імпортувати з Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "Upload File",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Failed to copy to the clipboard.",
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
"organizers": "Organizers",
@@ -367,7 +369,9 @@
"recipe-rules": "Recipe Rules",
"applies-to-all-days": "Applies to all days",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"meal-plan-settings": "Meal Plan Settings",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "Migration data removed",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Import original keywords as tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Stay in Edit mode",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Import from Zip",

View File

@@ -212,6 +212,8 @@
"upload-file": "上传文件",
"created-on-date": "创建于: {0}",
"unsaved-changes": "你有未保存的更改。你希望现在离开前保存吗?保存选择“是”,不保存选择“取消”。",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "未能复制到剪切板。",
"confirm-delete-generic-items": "你确定删除以下条目吗?",
"organizers": "管理器",
@@ -367,7 +369,9 @@
"recipe-rules": "食谱规则",
"applies-to-all-days": "应用到所有日期",
"applies-on-days": "应用于{0}",
"meal-plan-settings": "饮食计划设置"
"meal-plan-settings": "饮食计划设置",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "已删除迁移数据",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "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",
"stay-in-edit-mode": "留在编辑模式",
"parse-recipe-ingredients-after-import": "导入后解析食材",
"import-from-zip": "从Zip压缩包导入",

View File

@@ -212,6 +212,8 @@
"upload-file": "上傳文件",
"created-on-date": "Created on: {0}",
"unsaved-changes": "You have unsaved changes. Do you want to save before leaving? Okay to save, Cancel to discard changes.",
"discard-changes": "Discard Changes",
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
"clipboard-copy-failure": "Failed to copy to the clipboard.",
"confirm-delete-generic-items": "Are you sure you want to delete the following items?",
"organizers": "Organizers",
@@ -367,7 +369,9 @@
"recipe-rules": "Recipe Rules",
"applies-to-all-days": "Applies to all days",
"applies-on-days": "Applies on {0}s",
"meal-plan-settings": "Meal Plan Settings"
"meal-plan-settings": "Meal Plan Settings",
"add-all-to-list": "Add All to List",
"add-day-to-list": "Add Day to List"
},
"migration": {
"migration-data-removed": "遷移數據已刪除",
@@ -640,6 +644,7 @@
"scrape-recipe-website-being-blocked": "Website being blocked?",
"scrape-recipe-try-importing-raw-html-instead": "Try importing the raw HTML instead.",
"import-original-keywords-as-tags": "Import original keywords as tags",
"import-original-categories": "Import original categories",
"stay-in-edit-mode": "Stay in Edit mode",
"parse-recipe-ingredients-after-import": "Parse recipe ingredients after import",
"import-from-zip": "Import from Zip",

View File

@@ -44,6 +44,7 @@ export interface QueryFilterJSONPart {
attributeName?: string | null;
relationalOperator?: RelationalKeyword | RelationalOperator | null;
value?: string | string[] | null;
[k: string]: unknown;
}
export interface SaveCookBook {
name: string;

View File

@@ -53,6 +53,7 @@ export interface QueryFilterJSONPart {
attributeName?: string | null;
relationalOperator?: RelationalKeyword | RelationalOperator | null;
value?: string | string[] | null;
[k: string]: unknown;
}
export interface PlanRulesSave {
day?: PlanRulesDay;

View File

@@ -40,3 +40,20 @@ export enum Organizer {
Household = "households",
User = "users",
}
export type LogicalOperator = "AND" | "OR";
export type RelationalKeyword = "IS" | "IS NOT" | "IN" | "NOT IN" | "CONTAINS ALL" | "LIKE" | "NOT LIKE";
export type RelationalOperator = "=" | "<>" | ">" | "<" | ">=" | "<=";
export interface QueryFilterJSON {
parts?: QueryFilterJSONPart[];
}
export interface QueryFilterJSONPart {
leftParenthesis?: string | null;
rightParenthesis?: string | null;
logicalOperator?: LogicalOperator | null;
attributeName?: string | null;
relationalOperator?: RelationalKeyword | RelationalOperator | null;
value?: string | string[] | null;
}

View File

@@ -16,7 +16,7 @@ export interface OpenAIIngredients {
}
export interface OpenAIRecipe {
name: string;
description: string | null;
description?: string | null;
recipe_yield?: string | null;
total_time?: string | null;
prep_time?: string | null;
@@ -37,4 +37,7 @@ export interface OpenAIRecipeNotes {
title?: string | null;
text: string;
}
export interface OpenAIText {
text: string;
}
export interface OpenAIBase {}

View File

@@ -502,13 +502,16 @@ export interface SaveIngredientUnit {
}
export interface ScrapeRecipe {
includeTags?: boolean;
includeCategories?: boolean;
url: string;
}
export interface ScrapeRecipeBase {
includeTags?: boolean;
includeCategories?: boolean;
}
export interface ScrapeRecipeData {
includeTags?: boolean;
includeCategories?: boolean;
data: string;
url?: string | null;
}

View File

@@ -7,9 +7,6 @@
export type OrderByNullPosition = "first" | "last";
export type OrderDirection = "asc" | "desc";
export type LogicalOperator = "AND" | "OR";
export type RelationalKeyword = "IS" | "IS NOT" | "IN" | "NOT IN" | "CONTAINS ALL" | "LIKE" | "NOT LIKE";
export type RelationalOperator = "=" | "<>" | ">" | "<" | ">=" | "<=";
export interface ErrorResponse {
message: string;
@@ -28,17 +25,6 @@ export interface PaginationQuery {
page?: number;
perPage?: number;
}
export interface QueryFilterJSON {
parts?: QueryFilterJSONPart[];
}
export interface QueryFilterJSONPart {
leftParenthesis?: string | null;
rightParenthesis?: string | null;
logicalOperator?: LogicalOperator | null;
attributeName?: string | null;
relationalOperator?: RelationalKeyword | RelationalOperator | null;
value?: string | string[] | null;
}
export interface RecipeSearchQuery {
cookbook?: string | null;
requireAllCategories?: boolean;

View File

@@ -146,12 +146,12 @@ export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
return await this.requests.post<Recipe | null>(routes.recipesTestScrapeUrl, { url, useOpenAI });
}
async createOneByHtmlOrJson(data: string, includeTags: boolean, url: string | null = null) {
return await this.requests.post<string>(routes.recipesCreateFromHtmlOrJson, { data, includeTags, url });
async createOneByHtmlOrJson(data: string, includeTags: boolean, includeCategories: boolean, url: string | null = null) {
return await this.requests.post<string>(routes.recipesCreateFromHtmlOrJson, { data, includeTags, includeCategories, url });
}
async createOneByUrl(url: string, includeTags: boolean) {
return await this.requests.post<string>(routes.recipesCreateUrl, { url, includeTags });
async createOneByUrl(url: string, includeTags: boolean, includeCategories: boolean) {
return await this.requests.post<string>(routes.recipesCreateUrl, { url, includeTags, includeCategories });
}
async createManyByUrl(payload: CreateRecipeByUrlBulk) {

View File

@@ -24,8 +24,9 @@ export default defineNuxtConfig({
devtools: {
enabled: false,
},
app: {
baseURL: process.env.SUB_PATH || "",
baseURL: process.env.SUB_PATH || "/",
head: {
title: "Mealie",
@@ -218,7 +219,9 @@ export default defineNuxtConfig({
},
workbox: {
navigateFallback: "/",
navigateFallbackAllowlist: [/^(?!\/api)/],
globPatterns: ["**/*.{js,css,html,png,svg,ico}"],
globIgnores: ["404.html", "200.html"],
cleanupOutdatedCaches: true,
skipWaiting: true,
clientsClaim: true,
@@ -389,6 +392,23 @@ export default defineNuxtConfig({
locale: "en-US",
fallback: "en-US",
},
defaults: {
VOverlay: {
scrollStrategy: "close",
},
VMenu: {
scrollStrategy: "close",
},
VAutocomplete: {
scrollStrategy: "close",
},
VCombobox: {
scrollStrategy: "close",
},
VSelect: {
scrollStrategy: "close",
},
},
},
},
});

View File

@@ -1,6 +1,6 @@
{
"name": "mealie",
"version": "3.9.2",
"version": "3.10.2",
"private": true,
"scripts": {
"dev": "nuxt dev",

View File

@@ -35,6 +35,7 @@
{{ $t("general.create") }}
</BaseButton>
<BaseButton
v-if="$appInfo.allowPasswordLogin"
class="mr-2"
color="info"
:icon="$globals.icons.link"

View File

@@ -1,7 +1,7 @@
<template>
<v-form
ref="domUrlForm"
@submit.prevent="createFromHtmlOrJson(newRecipeData, importKeywordsAsTags, newRecipeUrl)"
@submit.prevent="createFromHtmlOrJson(newRecipeData, importKeywordsAsTags, importCategories, newRecipeUrl)"
>
<div>
<v-card-title class="headline">
@@ -63,6 +63,12 @@
hide-details
:label="$t('recipe.import-original-keywords-as-tags')"
/>
<v-checkbox
v-model="importCategories"
color="primary"
hide-details
:label="$t('recipe.import-original-categories')"
/>
<v-checkbox
v-model="stayInEditMode"
color="primary"
@@ -116,6 +122,7 @@ export default defineNuxtComponent({
const {
importKeywordsAsTags,
importCategories,
stayInEditMode,
parseRecipe,
navigateToRecipe,
@@ -160,7 +167,7 @@ export default defineNuxtComponent({
}
handleIsEditJson();
async function createFromHtmlOrJson(htmlOrJsonData: string | object | null, importKeywordsAsTags: boolean, url: string | null = null) {
async function createFromHtmlOrJson(htmlOrJsonData: string | object | null, importKeywordsAsTags: boolean, importCategories: boolean, url: string | null = null) {
if (!htmlOrJsonData) {
return;
}
@@ -179,7 +186,7 @@ export default defineNuxtComponent({
}
state.loading = true;
const { response } = await api.recipes.createOneByHtmlOrJson(dataString, importKeywordsAsTags, url);
const { response } = await api.recipes.createOneByHtmlOrJson(dataString, importKeywordsAsTags, importCategories, url);
handleResponse(response, importKeywordsAsTags);
}
@@ -188,6 +195,7 @@ export default defineNuxtComponent({
importKeywordsAsTags,
stayInEditMode,
parseRecipe,
importCategories,
newRecipeData,
newRecipeUrl,
handleIsEditJson,

View File

@@ -2,7 +2,7 @@
<div>
<v-form
ref="domUrlForm"
@submit.prevent="createByUrl(recipeUrl, importKeywordsAsTags)"
@submit.prevent="createByUrl(recipeUrl, importKeywordsAsTags, importCategories)"
>
<div>
<v-card-title class="headline">
@@ -38,6 +38,12 @@
hide-details
:label="$t('recipe.import-original-keywords-as-tags')"
/>
<v-checkbox
v-model="importCategories"
color="primary"
hide-details
:label="$t('recipe.import-original-categories')"
/>
<v-checkbox
v-model="stayInEditMode"
color="primary"
@@ -148,6 +154,7 @@ export default defineNuxtComponent({
const {
importKeywordsAsTags,
importCategories,
stayInEditMode,
parseRecipe,
navigateToRecipe,
@@ -219,7 +226,7 @@ export default defineNuxtComponent({
router.replace({ query: undefined }).then(() => router.push(to));
});
async function createByUrl(url: string | null, importKeywordsAsTags: boolean) {
async function createByUrl(url: string | null, importKeywordsAsTags: boolean, importCategories: boolean) {
if (url === null) {
return;
}
@@ -229,7 +236,7 @@ export default defineNuxtComponent({
return;
}
state.loading = true;
const { response } = await api.recipes.createOneByUrl(url, importKeywordsAsTags);
const { response } = await api.recipes.createOneByUrl(url, importKeywordsAsTags, importCategories);
handleResponse(response, importKeywordsAsTags);
}
@@ -238,6 +245,7 @@ export default defineNuxtComponent({
htmlOrJsonImporterTarget,
recipeUrl,
importKeywordsAsTags,
importCategories: importCategories,
stayInEditMode,
parseRecipe,
domUrlForm,

View File

@@ -422,7 +422,7 @@ import { Organizer } from "~/lib/api/types/non-generated";
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
import RecipeSuggestion from "~/components/Domain/Recipe/RecipeSuggestion.vue";
import SearchFilter from "~/components/Domain/SearchFilter.vue";
import type { QueryFilterJSON } from "~/lib/api/types/response";
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
import { useRecipeFinderPreferences } from "~/composables/use-users/preferences";

View File

@@ -1,5 +1,11 @@
<template>
<v-container>
<RecipeDialogAddToShoppingList
v-if="shoppingLists"
v-model="state.shoppingListDialog"
:recipes="weekRecipesWithScales"
:shopping-lists="shoppingLists"
/>
<v-menu
v-model="state.picker"
:close-on-content-click="false"
@@ -45,13 +51,23 @@
<div class="d-flex flex-wrap align-center justify-space-between mb-2">
<v-tabs style="width: fit-content;">
<v-tab :to="{ name: 'household-mealplan-planner-view', query: route.query }">
<v-tab :to="{ name: TABS.view, query: route.query }">
{{ $t('meal-plan.meal-planner') }}
</v-tab>
<v-tab :to="{ name: 'household-mealplan-planner-edit', query: route.query }">
<v-tab :to="{ name: TABS.edit, query: route.query }">
{{ $t('general.edit') }}
</v-tab>
</v-tabs>
<BaseButton
v-if="route.name === TABS.view"
color="info"
:icon="$globals.icons.cartCheck"
:text="$t('meal-plan.add-all-to-list')"
:disabled="!hasRecipes"
:loading="state.addAllLoading"
class="ml-auto mr-4"
@click="addAllToList"
/>
<ButtonLink
:icon="$globals.icons.calendar"
:to="`/household/mealplan/settings`"
@@ -72,15 +88,27 @@
<script lang="ts">
import { isSameDay, addDays, parseISO, format, isValid } from "date-fns";
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
import { useHouseholdSelf } from "~/composables/use-households";
import { useMealplans } from "~/composables/use-group-mealplan";
import { useUserMealPlanPreferences } from "~/composables/use-users/preferences";
import type { ShoppingListSummary } from "~/lib/api/types/household";
import { useUserApi } from "~/composables/api";
export default defineNuxtComponent({
components: {
RecipeDialogAddToShoppingList,
},
setup() {
const TABS = {
view: "household-mealplan-planner-view",
edit: "household-mealplan-planner-edit",
};
const route = useRoute();
const router = useRouter();
const i18n = useI18n();
const api = useUserApi();
const { household } = useHouseholdSelf();
useSeoMeta({
@@ -96,7 +124,7 @@ export default defineNuxtComponent({
// Force to /view if current route is /planner
if (route.path === "/household/mealplan/planner") {
router.push({
name: "household-mealplan-planner-view",
name: TABS.view,
query: route.query,
});
}
@@ -120,8 +148,12 @@ export default defineNuxtComponent({
start: initialStartDate,
picker: false,
end: initialEndDate,
shoppingListDialog: false,
addAllLoading: false,
});
const shoppingLists = ref<ShoppingListSummary[]>();
const firstDayOfWeek = computed(() => {
return household.value?.preferences?.firstDayOfWeek || 0;
});
@@ -145,7 +177,7 @@ export default defineNuxtComponent({
watch(weekRange, (newRange) => {
// Keep current route name and params, just update the query
router.replace({
name: route.name || "household-mealplan-planner-view",
name: route.name || TABS.view,
params: route.params,
query: {
...route.query,
@@ -193,7 +225,41 @@ export default defineNuxtComponent({
});
});
const hasRecipes = computed(() => {
return mealsByDate.value.some(day => day.meals.some(meal => meal.recipe));
});
const weekRecipesWithScales = computed(() => {
const allRecipes: any[] = [];
for (const day of mealsByDate.value) {
for (const meal of day.meals) {
if (meal.recipe) {
allRecipes.push(meal.recipe);
}
}
}
return allRecipes.map(recipe => ({
scale: 1,
...recipe,
}));
});
async function getShoppingLists() {
const { data } = await api.shopping.lists.getAll(1, -1, { orderBy: "name", orderDirection: "asc" });
if (data) {
shoppingLists.value = data.items as ShoppingListSummary[] ?? [];
}
}
async function addAllToList() {
state.value.addAllLoading = true;
await getShoppingLists();
state.value.shoppingListDialog = true;
state.value.addAllLoading = false;
}
return {
TABS,
route,
state,
actions,
@@ -201,6 +267,10 @@ export default defineNuxtComponent({
weekRange,
firstDayOfWeek,
numberOfDays,
hasRecipes,
shoppingLists,
weekRecipesWithScales,
addAllToList,
};
},
});

View File

@@ -26,7 +26,7 @@
<ToggleState tag="article">
<template #activator="{ toggle, state }">
<v-btn
v-if="!state"
v-if="!state && $appInfo.allowPasswordLogin"
color="info"
class="mt-2 mb-n3"
@click="toggle"
@@ -37,7 +37,7 @@
{{ $t("settings.change-password") }}
</v-btn>
<v-btn
v-else
v-else-if="$appInfo.allowPasswordLogin"
color="info"
class="mt-2 mb-n3"
@click="toggle"
@@ -111,7 +111,7 @@
class="mt-10"
:title="$t('settings.change-password')"
/>
<v-card variant="outlined">
<v-card variant="outlined" style="border-color: lightgrey;">
<v-card-text class="pb-0">
<v-form ref="passChange">
<v-text-field

View File

@@ -295,6 +295,7 @@ export default defineNuxtComponent({
async setup() {
const i18n = useI18n();
const $auth = useMealieAuth();
const { $appInfo } = useNuxtApp();
const route = useRoute();
const groupSlug = computed(() => route.params.groupSlug || $auth.user.value?.groupSlug || "");
@@ -302,7 +303,18 @@ export default defineNuxtComponent({
title: i18n.t("settings.profile"),
});
const user = computed<UserOut | null>(() => $auth.user.value);
const user = computed<UserOut | null>(() => {
const authUser = $auth.user.value;
if (!authUser) return null;
// Override canInvite if password login is disabled
const canInvite = !$appInfo.allowPasswordLogin ? false : authUser.canInvite;
return {
...authUser,
canInvite,
};
});
const inviteDialog = ref(false);
const api = useUserApi();

View File

File diff suppressed because it is too large Load Diff

View File

@@ -110,6 +110,11 @@ class AppSettings(AppLoggingSettings):
BASE_URL: str = "http://localhost:8080"
"""trailing slashes are trimmed (ex. `http://localhost:8080/` becomes ``http://localhost:8080`)"""
@property
def is_default_base_url(self) -> bool:
"""Returns True if BASE_URL has not been changed from the default."""
return self.BASE_URL == "http://localhost:8080"
STATIC_FILES: str = str(PACKAGE_DIR / "frontend")
"""path to static files directory (ex. `mealie/dist`)"""
@@ -412,6 +417,11 @@ class AppSettings(AppLoggingSettings):
"""
The number of seconds to wait for an OpenAI request to complete before cancelling the request
"""
OPENAI_CUSTOM_PROMPT_DIR: str | None = None
"""
Path to a folder containing custom prompt files;
files are individually optional, each prompt name will fall back to the default if no custom file exists
"""
@property
def OPENAI_FEATURE(self) -> FeatureDetails:

View File

@@ -168,14 +168,20 @@ def auto_init(): # sourcery no-metrics
setattr(self, key, instance)
elif relation_dir == MANYTOONE and not use_list:
lookup_attr = get_attr
if isinstance(val, dict):
val = val.get(get_attr)
# Prefer primary key when provided to avoid ambiguous alternate-key lookups
if "id" in val and val["id"] is not None:
lookup_attr = "id"
val = val["id"]
else:
val = val.get(get_attr)
if val is None:
raise ValueError(f"Expected 'id' to be provided for {key}")
raise ValueError(f"Expected '{lookup_attr}' to be provided for {key}")
if isinstance(val, str | int | UUID):
stmt = select(relation_cls).filter_by(**{get_attr: val})
stmt = select(relation_cls).filter_by(**{lookup_attr: val})
instance = session.execute(stmt).scalars().one_or_none()
setattr(self, key, instance)
else:

View File

@@ -3,12 +3,12 @@
"server-error": "Vyskytla sa neočakávaná chyba"
},
"recipe": {
"unique-name-error": "Názov receptu musí byť unikátny",
"unique-name-error": "Názov receptu musí byť jedinečný",
"recipe-created": "Vytvorený recept",
"recipe-image-deleted": "Obrázok receptu zmazaný",
"recipe-image-deleted": "Obrázok receptu bol vymazaný",
"recipe-defaults": {
"ingredient-note": "1 šálka múky",
"step-text": "Kroky receptu, ako aj ďalšie polia na stránke receptu podporujú markdown syntax.\n\n**Pridanie odkazu**\n\n[Môj odkaz](https://demo.mealie.io)\n"
"step-text": "Postup receptu, ako aj ďalšie polia na stránke receptu podporujú markdown syntax.\n\n**Pridanie odkazu**\n\n[Môj odkaz](https://demo.mealie.io)\n"
},
"servings-text": {
"makes": "Porcie",
@@ -20,19 +20,19 @@
}
},
"mealplan": {
"no-recipes-match-your-rules": "Vaše pravidlá nespĺňa žiadny recept"
"no-recipes-match-your-rules": "Žiadne recepty nezodpovedajú vašim pravidlám"
},
"user": {
"user-updated": "Používateľ bol aktualizovaný",
"password-updated": "Heslo bolo aktualizované",
"invalid-current-password": "Aktuálne heslo neplatné",
"invalid-current-password": "Aktuálne heslo je neplatné",
"ldap-update-password-unavailable": "Aktualizácia hesla nie je možná, používateľ je spravovaný v LDAPe"
},
"group": {
"report-deleted": "Report bol odstránený."
"report-deleted": "Hlásenie bolo vymazané."
},
"exceptions": {
"permission_denied": "Nemáte povolenie na vykonanie tejto akcie",
"permission_denied": "Nemáte oprávnenia na vykonanie tejto akcie",
"recursive-recipe-link": "Recept nemôže odkazovať na seba, priamo alebo nepriamo",
"no-entry-found": "404 - Požadovaná stránka nebola nájdená",
"integrity-error": "Chyba integrity databázy",
@@ -69,8 +69,8 @@
"subject": "Pozvanie na pripojenie k Mealie",
"header_text": "Boli ste pozvaní!",
"message_top": "Boli ste pozvaní na pripojenie k Mealie.",
"message_bottom": "Prosím, kliknite na tlačidlo vyššie pre akceptáciu pozvánky.",
"button_text": "Prijat pozvanie"
"message_bottom": "Prosím, kliknite na tlačidlo vyššie pre prijatie pozvánky.",
"button_text": "Prijať pozvánku"
},
"test": {
"subject": "Testovací email Mealie",

View File

@@ -4,19 +4,19 @@
},
"recipe": {
"unique-name-error": "Назив рецепта мора бити јединствен",
"recipe-created": "Recipe Created",
"recipe-image-deleted": "Recipe image deleted",
"recipe-created": "Рецепт је направљен",
"recipe-image-deleted": "Слика рецепта је обрисана",
"recipe-defaults": {
"ingredient-note": "1 Cup Flour",
"step-text": "Recipe steps as well as other fields in the recipe page support markdown syntax.\n\n**Add a link**\n\n[My Link](https://demo.mealie.io)\n"
"ingredient-note": "1 шоља брашна",
"step-text": "Упутство за рецепт као и остала поља подржавају markdown синтаксу\n\n**Додати линк**\n\n[Мој линк](https://demo.mealie.io)\n"
},
"servings-text": {
"makes": "Makes",
"serves": "Serves",
"serving": "Serving",
"servings": "Servings",
"yield": "Yield",
"yields": "Yields"
"makes": "Прави",
"serves": "Послужује",
"serving": "Порција",
"servings": "Број порција",
"yield": "Количина",
"yields": "Количина"
}
},
"mealplan": {
@@ -29,55 +29,55 @@
"ldap-update-password-unavailable": "Лозинку није могуће ажурирати, корисник је контролисан од стране LDAP-а"
},
"group": {
"report-deleted": "Рецепт је избрисан."
"report-deleted": "Извештај је избрисан."
},
"exceptions": {
"permission_denied": "Немате дозволу за извршење ове радње",
"recursive-recipe-link": "A recipe cannot reference itself, either directly or indirectly",
"recursive-recipe-link": "Рецепт не може да референцира сам себе, директно или индиректно",
"no-entry-found": "Захтевани ресурс није пронађен",
"integrity-error": "Грешка у интегритету базе података",
"username-conflict-error": "Корисничко име је већ заузето",
"email-conflict-error": "Овај е-мејл је већ у употреби"
"email-conflict-error": "Овај имејл је већ у употреби"
},
"notifications": {
"generic-created": "{name} је крериран",
"generic-created": "{name} је креиран",
"generic-updated": "{name} је ажуриран",
"generic-created-with-url": "{name} је направљено, {url}",
"generic-updated-with-url": "{name} је ажурирано, {url}",
"generic-duplicated": "{name} је дуплиран",
"generic-deleted": "{name} је обрисан",
"logged-out": "Logged out"
"logged-out": "Одјављено"
},
"datetime": {
"year": "year|years",
"day": "day|days",
"hour": "hour|hours",
"minute": "minute|minutes",
"second": "second|seconds",
"millisecond": "millisecond|milliseconds",
"microsecond": "microsecond|microseconds"
"year": "година",
"day": "дан",
"hour": "сат",
"minute": "минут",
"second": "секунд",
"millisecond": "милисекунд",
"microsecond": "микросекунд"
},
"emails": {
"password": {
"subject": "Mealie Forgot Password",
"header_text": "Forgot Password",
"message_top": "You have requested to reset your password.",
"message_bottom": "Please click the button above to reset your password.",
"button_text": "Reset Password"
"subject": "Mealie Заборављена лозинка",
"header_text": "Заборављена лозинка",
"message_top": "Направили сте захтев за ресетовање лозинке.",
"message_bottom": "Молимо вас да притиснете дугме изнад како бисте ресетовали лозинку.",
"button_text": "Ресетуј лозинку"
},
"invitation": {
"subject": "Invitation to join Mealie",
"header_text": "You're Invited!",
"message_top": "You have been invited to join Mealie.",
"message_bottom": "Please click the button above to accept the invitation.",
"button_text": "Accept Invitation"
"subject": "Mealie Позивница за придруживање",
"header_text": "Позвани сте!",
"message_top": "Позвани сте да се придружите Mealie-у.",
"message_bottom": "Молимо вас да притиснете дугме изнад како бисте прихватили позив.",
"button_text": "Прихвати позивницу"
},
"test": {
"subject": "Mealie Test Email",
"header_text": "Test Email",
"message_top": "This is a test email.",
"message_bottom": "Please click the button above to test the email.",
"button_text": "Open Mealie"
"subject": "Mealie тест имејл",
"header_text": "Пробни имејл",
"message_top": "Ово је пробни имејл.",
"message_bottom": "Молимо вас да притиснете дугме изнад како бисте тестирали имејл.",
"button_text": "Отвори Maelie"
}
}
}

View File

@@ -24,8 +24,8 @@ from mealie.schema.response.pagination import (
PaginationQuery,
RequestQuery,
)
from mealie.schema.response.query_filter import QueryFilterBuilder
from mealie.schema.response.query_search import SearchFilter
from mealie.services.query_filter.builder import QueryFilterBuilder
from ._utils import NOT_SET, NotSet

View File

@@ -26,7 +26,7 @@ from mealie.schema.recipe.recipe_ingredient import IngredientFood
from mealie.schema.recipe.recipe_suggestion import RecipeSuggestionQuery, RecipeSuggestionResponseItem
from mealie.schema.recipe.recipe_tool import RecipeToolOut
from mealie.schema.response.pagination import PaginationQuery
from mealie.schema.response.query_filter import QueryFilterBuilder
from mealie.services.query_filter.builder import QueryFilterBuilder
from ..db.models._model_base import SqlAlchemyBase
from .repository_generic import HouseholdRepositoryGeneric

View File

File diff suppressed because it is too large Load Diff

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